idm*_*ean 14 c++ gcc clang language-lawyer c++11
我有一个这样定义的类:
class ASTConcatenateLiteral : public ASTExpr {
using ASTExpr::ASTExpr;
private:
Type type_ = Type::nothingness(); // Type does not have a default constructor
};
Run Code Online (Sandbox Code Playgroud)
这对Clang很好用.但是,GCC给了我一条错误消息,让我觉得它正在尝试使用默认初始值设定项:
错误:没有用于调用'EmojicodeCompiler :: Type :: Type()'的匹配函数
如果我using ASTExpr::ASTExpr;用这样的公共构造函数替换(ASTExpr只提供这个构造函数)
ASTConcatenateLiteral(const SourcePosition &p) : ASTExpr(p) {}
Run Code Online (Sandbox Code Playgroud)
一切正常.
继承的构造函数等效于用户定义的构造函数,它们具有空主体,并且具有由单个嵌套名称说明符组成的成员初始化程序列表,该列表将所有参数转发给基类构造函数.
那么为什么继承的构造函数不起作用呢?如果继承的构造函数的行为类似于用户定义的构造函数,它应该使用提供的值type_,对吧?根据标准,哪个编译器是正确的?
son*_*yao 13
铿锵是对的.当使用继承的构造函数时,初始化应该继续进行,就像使用默认的默认构造函数初始化派生类的对象一样,因此应该使用默认的成员初始值设定项.
(强调我的)
如果重载决策在初始化此类派生类的对象时选择其中一个继承的构造函数,则使用继承的构造函数初始化从中继承构造函数的Base子对象,并且Derived的所有其他基础和成员都被初始化,就像默认情况一样默认构造函数(如果提供,则使用默认成员初始值设定项,否则将进行默认初始化).
这是标准的一个例子:
Run Code Online (Sandbox Code Playgroud)struct B1 { B1(int, ...) { } }; struct B2 { B2(double) { } }; int get(); struct D1 : B1 { using B1::B1; // inherits B1(int, ...) int x; int y = get(); }; void test() { D1 d(2, 3, 4); // OK: B1 is initialized by calling B1(2, 3, 4), // then d.x is default-initialized (no initialization is performed), // then d.y is initialized by calling get() D1 e; // error: D1 has a deleted default constructor }
请注意,d.y它由默认成员初始化程序初始化.
小智 11
这被GCC开发人员视为编译器错误PR67054,已在GCC 7.2中修复.
Run Code Online (Sandbox Code Playgroud)struct A { A(int) {} }; struct C { C(int) {} }; struct B : A { using A::A; C c = 42; }; int main() { B b = 24; }
在线编译器可用于验证GCC和clang现在已达成一致.
请注意,在GCC 7.1中,据我所知,不会生成错误的代码.虽然编译器需要默认构造函数,但永远不会调用该默认构造函数.因此,旧版GCC的可能解决方法是提供默认构造函数的声明,但不提供定义.甚至可能使用属性(不是标准C++)来拒绝任何使用它的尝试:
struct C
{
#ifdef GCC_WORKAROUND_PR67054
C() __attribute__((__error__("No.")));
#endif
C(int) {}
};
Run Code Online (Sandbox Code Playgroud)