【问题标题】:constexpr and static member declaration with templete class带有模板类的 constexpr 和静态成员声明
【发布时间】:2013-01-19 22:42:46
【问题描述】:

请看代码:

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

【问题讨论】:

  • 对我来说似乎很像一个错误,因为这一行编译没有问题:static_assert(std::is_same&lt;C&lt;5&gt;*, typename C&lt;5&gt;::this_type_1*&gt;::value, "Error!");。让我怀疑的是 Clang 3.2 和 GCC 4.7.2 都在抱怨。 VS2012有人吗?
  • 编译器可能不应该接受第三种变体template &lt;int N&gt; typename identity&lt;C&lt;N&gt;*&gt;::type C&lt;N&gt;::p_1;。但是这条线到底在哪里?

标签: c++ templates static declaration constexpr


【解决方案1】:

C&lt;N&gt;::p_1 的两个可能定义并不像看起来那么等价,因为C&lt;N&gt;::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&lt;N&gt;::p_1 的定义,则其声明的类型可能不正确。

【讨论】:

    【解决方案2】:

    属于错误 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&lt;N&gt;::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)用作模板参数有关。

    【讨论】:

      猜你喜欢
      • 2017-07-02
      • 1970-01-01
      • 2017-01-15
      • 1970-01-01
      • 2017-10-20
      • 2011-02-24
      • 1970-01-01
      • 2021-08-07
      相关资源
      最近更新 更多