继承构造函数在 C++ 中有多大用处?

当我参加 C++ 标准委员会会议时,他们正在讨论放弃 继承构造函数,因为还没有编译器供应商实现它(感觉用户还没有要求它).

As I sit in the C++ Standards committee meetings, they are discussing the pros and cons of dropping Inheriting Constructors since no compiler vendor has implemented it yet (the sense being users haven't been asking for it).

让我快速提醒大家什么是继承构造函数:

Let me quickly remind everyone what inheriting constructors are:

struct B
{
   B(int);
};

struct D : B
{
  using B::B;
};

一些供应商建议使用 r 值引用和可变参数模板(完美的转发构造函数),在继承类中提供转发构造函数可以避免继承构造函数.

Some vendors are proposing that with r-value references and variadic templates (perfect forwarding constructors), it would be trivial to provide a forwarding constructor in the inheriting class that would obviate inheriting constructors.

例如:

struct D : B
{
  template<class ... Args> 
    D(Args&& ... args) : B(args...) { } 
};

我有两个问题:

1) 您能否从您的编程经验中提供真实的(非人为的)示例,这些示例将从继承构造函数中受益匪浅?

1) Can you provide real world (non-contrived) examples from your programming experience that would benefit significantly from inheriting constructors?

2) 是否有任何您能想到的技术原因会妨碍完美转发构造函数"成为合适的替代方案?

2) Are there any technical reasons you can think of that would preclude "perfect forwarding constructors" from being an adequate alternative?

谢谢!

推荐答案

2) 是否有任何您能想到的技术原因会妨碍完美转发构造函数"成为合适的替代方案?

2) Are there any technical reasons you can think of that would preclude "perfect forwarding constructors" from being an adequate alternative?

我在这里展示了完美转发方法的一个问题:转发所有构造函数在 C++0x 中.

I have shown one problem with that perfect forwarding approach here: Forwarding all constructors in C++0x .

此外,完美的转发方法不能转发"基类构造函数的明确性:要么始终是转换构造函数,要么永远不会,基类将始终直接初始化(始终使用所有构造函数,甚至是显式的).

Also, the perfect forwarding approach can't "forward" the expliciteness of base-class constructors: Either it is always a converting constructor or never, and the base-class will always be direct initialized (always making use of all constructors, even explicit ones).

另一个问题是初始化列表构造函数,因为你不能将 Args 推导出到 initializer_list.相反,您需要使用 B{args...}(注意大括号)转发到基础,并使用 (a, b,c){1, 2, 3}= {1, 2, 3}.在这种情况下, Args 将是初始化列表的元素类型,并将它们转发给基类.然后初始化列表构造函数可以接收它们.这似乎会导致不必要的代码膨胀,因为模板参数包可能会为每个不同的类型和长度组合包含大量类型序列,并且因为您必须选择初始化语法,这意味着:

Another problem are initializer-list constructors because you can't deduce Args to initializer_list<U>. Instead, you would need to forward to the base with B{args...} (note the braces) and initialize D objects with (a, b, c) or {1, 2, 3} or = {1, 2, 3}. In that case, Args would be the element types of the initializer list, and forward them to the base class. A initializer-list constructor can then receive them. This seems to cause unnecessary code bloat because the template argument pack will potentially contain lots of type sequences for each different combination of types and length and because you have to choose an initialization syntax this means:

struct MyList {
  // initializes by initializer list
  MyList(std::initializer_list<Data> list);

  // initializes with size copies of def
  MyList(std::size_t size, Data def = Data());
};

MyList m{3, 1}; // data: [3, 1]
MyList m(3, 1); // data: [1, 1, 1]

// either you use { args ... } and support initializer lists or
// you use (args...) and won't
struct MyDerivedList : MyList {
  template<class ... Args> 
  MyDerivedList(Args&& ... args) : MyList{ args... } { } 
};

MyDerivedList m{3, 1}; // data: [3, 1]
MyDerivedList m(3, 1); // data: [3, 1] (!!)

相关文章