具有模板专业化的 constexpr 数组成员:不一致的行为交叉编译器
constexpr array member with template specialization: inconsistent behavior cross compilers
考虑以下代码:
#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 那里读到
A constexpr specifier used in a function or static member variable (since C++17) declaration implies inline.
因此,模板专业化 foo<int>
似乎没有理由失败。此外,我在链接之前查看了生成的目标文件,对 foo<int>::value[2];
的访问是 而不是 在编译时完成的,正如人们所期望的那样。我高度怀疑 clang++ 编译器有问题。
2) 如何处理这个 clang++ 链接错误?
我尝试了类似 Undefined reference to static constexpr char[] 的方法,但最终我找不到任何方法来克服这个链接错误。所以我只是想知道是否有办法让这个链接成功。
直到C++17,原因与the question you found (I think the answer posted by Shafik Yaghmour explains this problem more exactly). In short, the definition of a constexpr
static data member is still required if the member is odr-used.
中所述完全相同
您可以 resolve the problem 通过提供这些变量的定义 直到 C++17(即使用 -std=c++14
)。
自 C++17 起,[dcl.constexpr] paragraph 1 中的当前标准表示
... A function or static data member declared with the constexpr
specifier is implicitly an inline function or variable ([dcl.inline]).
A declaration is a definition unless
...
it declares a non-inline static data member in a class definition ([class.mem], [class.static]),
...
因此不需要命名空间范围内的此类定义。
此外,[depr.static_constexpr] paragraph 1中的当前标准说
For compatibility with prior C++ International Standards, a constexpr static data member may be redundantly redeclared outside the class with no initializer. This usage is deprecated. [ Example:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
— end example ]
所以从 C++17 开始你最好避免这样的定义。
When I change to clang++ -std=c++17
, then only one undefined symbol: foo<int>::value
.
这是一个 Clang 错误。无论如何,它 works well 用于 Clang HEAD 7.0.0。
考虑以下代码:
#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 那里读到
A constexpr specifier used in a function or static member variable (since C++17) declaration implies inline.
因此,模板专业化 foo<int>
似乎没有理由失败。此外,我在链接之前查看了生成的目标文件,对 foo<int>::value[2];
的访问是 而不是 在编译时完成的,正如人们所期望的那样。我高度怀疑 clang++ 编译器有问题。
2) 如何处理这个 clang++ 链接错误?
我尝试了类似 Undefined reference to static constexpr char[] 的方法,但最终我找不到任何方法来克服这个链接错误。所以我只是想知道是否有办法让这个链接成功。
直到C++17,原因与the question you found (I think the answer posted by Shafik Yaghmour explains this problem more exactly). In short, the definition of a constexpr
static data member is still required if the member is odr-used.
您可以 resolve the problem 通过提供这些变量的定义 直到 C++17(即使用 -std=c++14
)。
自 C++17 起,[dcl.constexpr] paragraph 1 中的当前标准表示
... A function or static data member declared with the
constexpr
specifier is implicitly an inline function or variable ([dcl.inline]).
A declaration is a definition unless
...
it declares a non-inline static data member in a class definition ([class.mem], [class.static]),
...
因此不需要命名空间范围内的此类定义。
此外,[depr.static_constexpr] paragraph 1中的当前标准说
For compatibility with prior C++ International Standards, a constexpr static data member may be redundantly redeclared outside the class with no initializer. This usage is deprecated. [ Example:
struct A { static constexpr int n = 5; // definition (declaration in C++ 2014) }; constexpr int A::n; // redundant declaration (definition in C++ 2014)
— end example ]
所以从 C++17 开始你最好避免这样的定义。
When I change to
clang++ -std=c++17
, then only one undefined symbol:foo<int>::value
.
这是一个 Clang 错误。无论如何,它 works well 用于 Clang HEAD 7.0.0。