奇怪的未初始化常量成员行为

17
考虑以下代码片段:
struct Foo {

};

template<typename T>
struct Bar {
    const T foo;
};

int main() {
    Bar<Foo> test;
}

我正在使用g++-4.9.2编译它,使用[-std=c++11 -O0 -g3 -pedantic -Wall -Wextra -Wconversion]并得到错误:'error: uninitialized const member in ‘struct Bar<Foo>’。这很明显。但是,尝试将std::string作为Foo成员添加,程序就可以编译了!
#include <string>
struct Foo {
    std::string test;
};
// (...)

发生了什么?将测试的类型更改为double会导致程序再次无法编译。类中哪个字符串成员发生了变化?

链接到包含此代码片段的在线编译器。

自从4.6版本以来,gcc似乎就是这样的行为。

7
似乎是GCC的一个错误,而clang没有这个问题:http://coliru.stacked-crooked.com/a/508312aef603cc15 - Deduplicator
我最初认为这是一些缺失的警告,但添加[-O0 -g3 -pedantic -Wall -Wextra -Wconversion]并没有给出更多的编译器消息。 - omikron
MSVC也没有出现这个问题,并且因缺少默认构造函数而拒绝编译。 - Serge Ballesta
3个回答

18

我认为应该始终产生错误,Clang也是如此。 C ++标准在§12.1.4的第(4.3)条款中指出,当

—任何const限定类型(或其数组)的非变量非静态数据成员没有大括号或等于初始化程序时,没有用户提供的默认构造函数,

由于Foo没有用户提供的默认构造函数,因此Bar应该具有隐式删除的默认构造函数,因此在主函数中实例化Bar<Foo> test应该产生一个错误。

也许向GCC报告错误?


4
如果你的类/结构体中有一个const数据成员,那么编译器不会为其生成默认构造函数。你需要显式定义并初始化该const成员(而非赋值)。无论哪种情况,这都应该是一个错误。

4
这并没有回答问题。如果给Foo添加一个std::string数据成员,为什么它会编译通过? - juanchopanza
“Bar”有一个名为“Foo”的“const”数据成员。 - juanchopanza
@ravi:Foo字符串参数的常量性并不重要。Bar有一个const Foo - omikron
这对我来说在两种情况下都是错误(Apple LLVM 6.0)。您不能定义未初始化的const变量,并且默认构造函数不能应用于这种情况,您需要在Foo类中定义一个无参ctor。 - Jean-Baptiste Yunès

2

看起来 g++ 在 const 成员应该在构造时初始化的情况下,即使知道字符串具有将其初始化为空字符串的默认构造函数,也会自动生成默认构造函数。实际上,所有操作都像源代码是这样的:

struct Foo {
    std::string test;
    Foo():test() {;}
};

template<typename T>
struct Bar {
    const T foo;
    Bar(): foo() {;}
};

int main() {
    Bar<Foo> test;
}

这段代码在clang和MSVC编译器下都可以顺利编译通过。

(但我必须承认我仍然没有在gcc文档中找到解释这一点的参考资料...)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接