2

コードを見てください:

#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++言語仕様に基づく理由を教えてください。

4

2 に答える 2

2

の 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宣言された型が正しくない可能性があります。

于 2013-01-19T23:28:02.230 に答える
0

これは、Clang (3.2) と GCC (4.7.2) の両方に影響するバグIMO とみなされます。私の主張は、次の証拠によって裏付けられています(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()式はコンパイル エラーを引き起こしません。つまり、2 つの型はまったく同じです。staticしかし、その場合、 memberを定義する 2 つの方法に違いはないはず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

于 2013-01-19T23:16:13.647 に答える