constexpr和带有模板类的静态成员声明

时间:2013-01-19 22:42:47

标签: c++ templates static declaration constexpr

请参阅代码:

#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 ++语言规范告诉我原因。

2 个答案:

答案 0 :(得分:2)

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的定义,则其声明的类型可能不正确。

答案 1 :(得分:0)

有资格作为错误 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)作为模板参数有关。