继承构造函数在C++;? 当我坐在C++标准委员会会议上时,他们讨论了放弃的利弊,因为没有编译器供应商实现它(用户没有请求它的意义)。
让我快速提醒大家什么是继承构造函数:继承构造函数在C++;? 当我坐在C++标准委员会会议上时,他们讨论了放弃的利弊,因为没有编译器供应商实现它(用户没有请求它的意义)。,c++,inheritance,constructor,c++11,perfect-forwarding,C++,Inheritance,Constructor,C++11,Perfect Forwarding,让我快速提醒大家什么是继承构造函数: struct B { B(int); }; struct D : B { using B::B; }; 一些供应商建议,对于r值引用和可变模板(完美的转发构造函数),在继承类中提供一个可以避免继承构造函数的转发构造函数是微不足道的 例如: struct D : B { template<class ... Args> D(Args&& ... args) : B(args...) { } }; 结构
struct B
{
B(int);
};
struct D : B
{
using B::B;
};
一些供应商建议,对于r值引用和可变模板(完美的转发构造函数),在继承类中提供一个可以避免继承构造函数的转发构造函数是微不足道的
例如:
struct D : B
{
template<class ... Args>
D(Args&& ... args) : B(args...) { }
};
结构D:B
{
模板
D(Args&&…Args):B(Args…{}
};
我有两个问题:
1) 您能否从您的编程经验中提供真实世界(非人为)的示例,这些示例将从继承构造函数中获得显著的好处
2) 是否有任何技术上的原因会阻止“完美转发构造函数”成为一个合适的替代方案
谢谢
2) 是否有任何技术上的原因会阻止“完美转发构造函数”成为一个合适的替代方案
我在这里展示了完美转发方法的一个问题:
此外,完美的转发方法不能“转发”基类构造函数的显式性:要么总是转换构造函数,要么从不,基类总是直接初始化(总是使用所有构造函数,甚至显式构造函数)
另一个问题是初始值设定项列表构造函数,因为您无法将Args
推断为initializer\u list
。相反,您需要使用B{args…}
(注意大括号)将D
对象与(a,B,c)
或{1,2,3}
或={1,2,3}
进行初始化。在这种情况下,Args
将是初始值设定项列表的元素类型,并将它们转发给基类。然后,初始值设定项列表构造函数可以接收它们。这似乎会导致不必要的代码膨胀,因为模板参数包可能会包含许多类型和长度的不同组合的类型序列,并且因为您必须选择初始化语法,这意味着:
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] (!!)
struct MyList{
//通过初始化器列表初始化
MyList(std::初始值设定项列表);
//使用def的大小副本初始化
MyList(std::size\u t size,Data def=Data());
};
MyList m{3,1};//数据:[3,1]
MyList m(3,1);//数据:[1,1,1]
//要么使用{args…}并支持初始值设定项列表,要么
//您使用(args…)并且不会
结构MyDerivedList:MyList{
模板
MyDerivedList(Args&&…Args):MyList{Args…}{}
};
MyDerivedList m{3,1};//数据:[3,1]
MyDerivedList m(3,1);//数据:[3,1](!!)
从哲学上讲,我反对继承构造函数。如果要定义一个新类,就要定义如何创建它。如果大部分构造可以在基类中进行,那么将该工作转发到初始化列表中基类的构造函数是完全合理的。但您仍然需要明确地这样做 建议的解决方案有几个缺点:
- 它更长
- 它有更多的代币
- 它使用了全新的复杂语言特征
构造函数继承的真实动机:使用重复继承而不是多重继承实现AOP mix ins。当新类具有需要在构造函数中初始化的成员变量时,我发现了一个问题。这是常见的情况,因为派生类通常会向基类添加某种状态 即:
struct B
{
B(int);
};
struct D : B
{
D(int a, int b) : B(a), m(b) {}
int m;
};
对于那些试图解决这个问题的人:你如何区分:B(a),m(B)
和:B(B),m(a)
?如何处理多重继承?虚拟继承
如果只解决最简单的情况,它在实践中的作用将非常有限。难怪编译器厂商还没有实现这个建议。
除了别人所说的以外,请考虑这个人为的例子:
#include <iostream>
class MyString
{
public:
MyString( char const* ) {}
static char const* name() { return "MyString"; }
};
class MyNumber
{
public:
MyNumber( double ) {}
static char const* name() { return "MyNumber"; }
};
class MyStringX: public MyString
{
public:
//MyStringX( char const* s ): MyString( s ) {} // OK
template< class ... Args >
MyStringX( Args&& ... args ): MyString( args... ) {} // !Nope.
static char const* name() { return "MyStringX"; }
};
class MyNumberX: public MyNumber
{
public:
//MyNumberX( double v ): MyNumber( v ) {} // OK
template< class ... Args >
MyNumberX( Args&& ... args ): MyNumber( args... ) {} // !Nope.
static char const* name() { return "MyNumberX"; }
};
typedef char YesType;
struct NoType { char x[2]; };
template< int size, class A, class B >
struct Choose_{ typedef A T; };
template< class A, class B >
struct Choose_< sizeof( NoType ), A, B > { typedef B T; };
template< class Type >
class MyWrapper
{
private:
static Type const& dummy();
static YesType accept( MyStringX );
static NoType accept( MyNumberX );
public:
typedef typename
Choose_< sizeof( accept( dummy() ) ), MyStringX, MyNumberX >::T T;
};
int main()
{
using namespace std;
cout << MyWrapper< int >::T::name() << endl;
cout << MyWrapper< char const* >::T::name() << endl;
}
#包括
类MyString
{
公众:
MyString(char const*){}
静态字符常量*name(){返回“MyString”;}
};
类别MyNumber
{
公众:
MyNumber(双精度){}
静态字符常量*name(){返回“MyNumber”;}
};
类MyStringX:public MyString
{
公众:
//MyStringX(char const*s):MyString{}//OK
模板<类…参数>
MyStringX(Args&&…Args):MyString(Args…{}/!Nope。
静态字符常量*name(){返回“MyStringX”;}
};
类别MyNumber X:公共MyNumber
{
公众:
//MyNumberX(双v):MyNumber(v){}//OK
模板<类…参数>
MyNumberX(Args&&…Args):MyNumberX(Args…{}/!Nope。
静态字符常量*name(){返回“MyNumberX”;}
};
typedef char-YesType;
结构NoType{char x[2];};
模板
结构选择{typedef A T;};
模板
结构选择{sizeof(NoType),A,B>{typedef B T;};
模板<类类型>
类MyWrapper
{
私人:
静态类型const&dummy();
静态YesType接受(MyStringX);
静态NoType接受(MyNumberX);
公众:
typedef typename
选择(accept(dummy())、MyStringX、MyNumberX>:T;
};
int main()
{
使用名称空间std;
cout::T::name()在那里玩得开心。让我们希望他们能处理所有这些关键问题:)@Johannes:谢谢!你的一些DRs已经被讨论过:)完美的转发构造函数的一个明显问题可能是它们实际上并不是很短。对于很多类,我可以只编写一个“传统”的转发构造函数