用模板类声明Constexpr和静态成员
constexpr and static member declaration with templete class
请看代码:
#include <iostream>
#include <typeinfo>
template<int N>
struct C
{
static constexpr int n = N;
using this_type_1 = C<n>;
using this_type_2 = C<N>;
static this_type_1* p_1;
static this_type_2* p_2;
};
template<int N>
//C<N>* C<N>::p_1; // <--- error pattern
typename C<N>::this_type_1* C<N>::p_1; // <--- ok pattern
template<int N>
C<N>* C<N>::p_2; // ok
int main(){
std::cerr
<< typeid(C<0>).name() << "n"
<< typeid(C<0>::this_type_1).name() << "n"
<< typeid(C<0>::this_type_2).name() << "n"
;
}
可以用g++-4.7.1和clang++-3.1编译。但是它不能用注释掉的错误模式编译。
g++错误信息为:
test.cpp:15:13: error: conflicting declaration ‘C<N>* C<N>::p_1’
test.cpp:10:23: error: ‘C<N>::p_1’ has a previous declaration as ‘C<N>::this_type_1* C<N>::p_1’
test.cpp:15:13: error: declaration of ‘C<N>::this_type_1* C<N>::p_1’ outside of class is not definition [-fpermissive]
clang++错误信息是:
test.cpp:15:13: error: redefinition of 'p_1' with a different type
C<N>* C<N>::p_1; // error
^
test.cpp:10:23: note: previous definition is here
static this_type_1* p_1;
^
1 error generated.
幸运的是,我发现了一个工作模式。但是我不知道为什么错误模式不能编译。请根据c++语言规范告诉我原因
C<N>::p_1
的两个可能的定义并不像它们看起来的那样等价,因为C<N>::n
可以在给定N
的第一次实例化之前的任何时间显式特化。
template<int N>
struct C
{
static constexpr int n = N;
using this_type_1 = C<n>;
static this_type_1* p_1;
};
template<int N>
C<N>* C<N>::p_1; // ERROR
template<>
constexpr int C<5>::n = 6;
int main()
{
C<6>* p = C<5>::p_1;
}
如果编译器接受了C<N>::p_1
的定义,那么它声明的类型就有可能是不正确的。
这个被认为是一个bug IMO,影响Clang(3.2)和GCC(4.7.2)。我的主张得到以下证据的支持(我试图将OP的问题中的代码减少到最小):
#include <type_traits>
template<int N>
struct C
{
static constexpr int n = N;
using T = C<n>;
static T* p;
};
// This compiles, which proves that (C<N>* == typename C<N>::T*)
static_assert(std::is_same<C<5>*, typename C<5>::T*>::value, "Error!");
template<int N>
typename C<N>::T* C<N>::p; // OK
// C<N>* C<N>::p; // ERROR! Contradicts the above hypothesis
int main()
{
}
static_assert()
表达式不会导致任何编译错误,这意味着这两种类型确实是相同的。但是,如果是这样的话,定义static
成员C<N>::p
的两种方法之间应该没有区别。
同样,这段代码编译:
#include <type_traits>
template<int N>
struct C
{
using T = C<N>;
static T* p;
};
// This compiles, which proves that (C<N>* == typename C<N>::T*)
static_assert(std::is_same<C<5>*, typename C<5>::T*>::value, "Error!");
template<int N>
C<N>* C<N>::p; // OK now
int main()
{
}
这意味着问题与使用constexpr
静态变量(本例中为n
)作为模板参数有关。
相关文章:
- 静态 constexpr 类成员变量对多线程读取是否安全?
- 不带初始值设定项的 constexpr 静态数据成员
- 在类外部初始化的 constexpr 静态成员的声明中是否需要 constexpr 说明符
- 现在允许重新定义 constexpr 静态数据成员吗?(但不是内联常量)?
- 定义 constexpr 静态数据成员
- constexpr静态成员什么时候停止成为constexpr
- 获取 constexpr 全局变量(不是静态成员)的链接器符号
- gcc 和 clang 中 constexpr 静态成员变量的链接器错误
- 静态 constexpr 模板成员在专用时提供未定义的引用
- Constexpr静态成员函数用法
- 重新声明静态 constexpr 数据成员?
- constexpr静态结构类成员的声明冲突
- 不接受静态成员函数作为 constexpr 参数
- 为什么调用此constexpr静态成员函数时不将其视为constexpr
- 为什么constexpr静态成员(类型为class)需要定义
- constexpr静态成员变量的异常行为
- constexpr 静态成员与变量
- 定义不同于 constexpr 静态成员的声明
- c++之前/之后的constexpr静态成员
- 完全专用模板的constexpr静态成员在CLang上的动态链接失败