请考虑以下代码:
#include <iostream>
template<class T>
struct foo {};
template<>
struct foo<int> {
static constexpr char value[] = "abcde";
};
template<class T>
struct bar {
static constexpr char value[] = "abcde";
};
template<class T>
struct baz {
static constexpr int value = 12345;
};
int main() {
char c = foo<int>::value[2];
char d = bar<int>::value[2];
int e = baz<int>::value;
std::cout << c << d << e << "\n";
}
使用:clang++ -std=c++14 ./test_foo.cc
进行编译时,我收到了未定义符号的链接器错误:bar<int>::value
和foo<int>::value
。当我更改为clang++ -std=c++17
时,只有一个未定义的符号:foo<int>::value
。我的clang ++版本是5.0。
然而,当我尝试g++ -std=c++14 ./test_foo.cc
时,编译成功。我的g ++版本是5.4.0。
我有两件事要问。
1)从C ++标准的角度来看,哪个编译器行为正确?
我用谷歌搜索并阅读了许多cppreference页面,但没有发现任何与此现象真正相关的内容。特别是对于使用-std=c++17
的clang ++,行为非常奇怪,因为bar<int>
已通过但foo<int>
失败,唯一的区别是foo<int>
是一个特化。我从http://en.cppreference.com/w/cpp/language/constexpr读到了
函数或静态成员变量(自C ++ 17)声明中使用的constexpr说明符暗示内联。
因此,似乎没有理由让模板特化foo<int>
失败。此外,我在链接之前查看了生成的目标文件,在编译时对foo<int>::value[2];
的访问不就像人们期望的那样。我非常怀疑clang ++编译器有什么问题。
2)如何处理这个clang ++链接错误?
我尝试过类似Undefined reference to static constexpr char[]的内容,但最后我无法找到任何方法来克服此链接错误。所以我只是想知道是否有办法让这种连接成功。
答案 0 :(得分:2)
直到C ++ 17,其原因与the question you found中所述的完全相同(我认为Shafik Yaghmour发布的答案更准确地解释了这个问题)。简而言之,如果成员是odr-used,则仍然需要定义constexpr
静态数据成员。
您可以resolve the problem提供这些变量的定义,直到C ++ 17 (即使用-std=c++14
)。
自C ++ 17以来,[dcl.constexpr] paragraph 1中的当前标准说
...使用
constexpr
说明符声明的函数或静态数据成员隐式地是内联函数或变量([dcl.inline])。
声明是定义,除非
...
它在类定义中声明了非内联静态数据成员([class.mem],[class.static]),
...
因此,不需要在命名空间范围内进行此类定义。
此外,[depr.static_constexpr] paragraph 1中的当前标准是
为了与先前的C ++国际标准兼容,constexpr静态数据成员可以在课外冗余重新声明而不使用初始化程序。不推荐使用此用法。 [实施例:
struct A { static constexpr int n = 5; // definition (declaration in C++ 2014) }; constexpr int A::n; // redundant declaration (definition in C++ 2014)
- 结束示例]
因此,从C ++ 17开始,你最好避免这种定义。
当我更改为
clang++ -std=c++17
时,只有一个未定义的符号:foo<int>::value
。
这是一个Clang错误。无论如何,对于Clang HEAD 7.0.0它works well。