4

私は新しい C++11 の「列挙型クラス」型を使用しましたが、g++ を使用すると「未定義の参照」の問題が発生しました。この問題は、clang++ では発生しません。私が何か間違ったことをしているのか、それとも g++ のバグなのかわかりません。

問題を再現するコードは次のとおりです: (4 つのファイル: enum.hpp、enum.cpp、main.cpp、および Makefile)

// file: enum.hpp
enum class MyEnum {
  val_1,
  val_2
};

template<typename T>
struct Foo 
{
  static const MyEnum value = MyEnum::val_1;
};

template<>
struct Foo<int>
{
  static const MyEnum value = MyEnum::val_2;
};

template<typename T>
void foo(const T&);

と...

// file: enum.cpp 
#include <iostream>
#include "enum.hpp"

template<typename T>
void foo(const T&)
{
  switch(Foo<T>::value) {
  case MyEnum::val_1:
    std::cout << "\n enum is val_1"; break;
  case MyEnum::val_2:
    std::cout << "\n enum is val_2"; break;
  default:
    std::cout << "\n unknown enum"; break;
  }
}

// Here we force instantation, thus everything should be OK!?!
//
template void foo<int>(const int&);
template void foo<double>(const double&);

と...

// file: main.cpp
#include "enum.hpp"

int
main()
{
  foo(2.);
  foo(2);
}

そしてメイクファイル...

COMPILER = g++ # does no work
#COMPILER = clang++ # Ok

all: main

main : main.cpp enum.cpp
    $(COMPILER) -std=c++11 -c enum.cpp -o enum.o
    $(COMPILER) -std=c++11 main.cpp enum.o -o main

g++ を使用していると、次のようになります。

make -k 
g++  -std=c++11 -c enum.cpp -o enum.o
g++  -std=c++11 main.cpp enum.o -o main
enum.o: In function `void foo<int>(int const&)':
enum.cpp:(.text._Z3fooIiEvRKT_[_Z3fooIiEvRKT_]+0xe): undefined reference to `Foo<int>::value'
enum.o: In function `void foo<double>(double const&)':
enum.cpp:(.text._Z3fooIdEvRKT_[_Z3fooIdEvRKT_]+0xe): undefined reference to `Foo<double>::value'
collect2: error: ld returned 1 exit status
make: *** [main] Error 1
make: Target `all' not remade because of errors.

しかし、clang++ ではすべて問題ありません (コンパイル エラーはありません)。

私はここで迷っているので、どんな説明でも大歓迎です。

ありがとう!:)


私の設定について:

g++ --version
g++ (Debian 4.7.2-5) 4.7.2
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

clang++ --version
Debian clang version 3.0-6 (tags/RELEASE_30/final) (based on LLVM 3.0)
Target: x86_64-pc-linux-gnu
Thread model: posix

uname -a
Linux IS006139 3.2.0-4-amd64 #1 SMP Debian 3.2.35-2 x86_64 GNU/Linux
4

2 に答える 2

2

これらのエラーが発生する理由は、g++ が静的変数がどこかに定義されていることを期待しているためです。

これを修正するには、いくつかの方法があります。

整数型を使用しているため、構造体を Integral_constant から継承するように変更できます。

template<typename T>
struct Foo : std::integral_constant<MyEnum, MyEnum::val_1>
{
};

template<>
struct Foo<int> : std::integral_constant<MyEnum, MyEnum::val_2>
{
};

変数 constexpr を宣言することもできます

template<typename T>
struct Foo
{
  static constexpr MyEnum value = MyEnum::val_1;
};

template<>
struct Foo<int>
{
  static constexpr MyEnum value = MyEnum::val_2;
};

ヘッダー ファイルで静的変数を定義できます。

template<typename T>
struct Foo
{
  static const MyEnum value = MyEnum::val_1;
};

template<typename T>
const MyEnum Foo<T>::value;

template<>
struct Foo<int>
{
  static const MyEnum value = MyEnum::val_2;
};

// enum.cpp
const MyEnum Foo<int>::value;
于 2013-01-22T17:20:54.010 に答える
1

これは g++ のバグです。データ メンバーがodr-usedstaticである場合、データ メンバーの定義が必要ですが、 orの唯一の言及は次のとおりです。Foo<int>::valueFoo<double>::value

switch(Foo<T>::value) {

[ basic.def.odr ]p2およびp3によると、次の理由により、これはODR 使用ではありません。Foo<T>::value

  • 左辺値から右辺値への変換はすぐに式に適用されFoo<T>::value
  • エンティティFoo<T>::valueは式の潜在的な結果のセットにありFoo<T>::value、かつ
  • Foo<T>::value定数式に出現するための要件を満たしている

したがって、このプログラムでは、 Foo<int>::valuenorの定義は必要ありません。Foo<double>::value

ただし、次のように常に静的データ メンバーを定義することをお勧めします。

// In your header file
template<typename T> const MyEnum Foo<T>::value;
// In your .cpp file
template<> const MyEnum Foo<int>::value;

テンプレートのユーザーはテンプレートをインスタンス化する必要があるため、最初のものはヘッダーに入れる必要があります。重複するインスタンス化はマージされます。2 番目のものはヘッダーに入れてはなりませ。このエンティティはテンプレート化されていないため、プログラム全体でこのエンティティの定義は 1 つしか存在できません (また、複数の定義を許可するインライン関数、クラス、または列挙定義でもありません)。異なる翻訳単位で)。

于 2013-05-20T06:36:34.817 に答える