2013-01-22 56 views
4

我使用了新的C++ 11「枚舉類」類型,並在使用g ++時觀察到「未定義參考」問題。這個問題在clang ++中不會發生。我不知道我是否做錯了什麼,或者如果它是一個g ++的錯誤。C++ 11,枚舉類,使用g ++的未定義參考,與clang一起使用++

要重現這裏的問題是代碼:(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); 
} 

和Makefile文件...

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 

當我使用克+ +我得到:

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. 

但隨着鏗鏘++一切正常(無編譯錯誤)。

任何解釋都是值得歡迎的,因爲我迷失在這裏。

謝謝! :)


關於我的配置:

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 
+0

可能是一個編譯器錯誤。我希望在'switch(constant_expression)'中,「將左值到右值的轉換立即應用於'constant_expression'並且它不是一個odr使用,但是這很難證明。 – aschepler

+0

我忘了提及的是,如果您刪除「class」關鍵字並使用舊式枚舉(enum MyEnum {val_1,val_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; 
+0

'const MyEnum Foo :: value;'不是專業化或定義。它是一個明確的實例化,因爲它可以隱式地實例化,所以沒有用處。 – aschepler

+0

謝謝你的快速回答。 我檢查你的建議: 1 /一用的std :: integral_constant工作正常 2 /在一個靜態變量的作品,但線 模板 常量MyEnum富 ::值; 必須在enum.cpp中,而不是在頭文件中enum.hpp 3 /不幸的是,constexpr(儘量減少代碼修改量)的解決方案仍然不起作用。也許是一個編譯器錯誤: -/?? –

+0

那麼....這不是一個編譯器錯誤....這裏是解釋:[鏈接](http://gcc.gnu.org/wiki/VerboseDiagnostics#missing_static_const_definition)。對不起,噪音 –

1

這是g ++中的一個bug。

switch(Foo<T>::value) { 

[basic.def:如果該數據成員是ODR使用的,但Foo<int>::valueFoo<double>::value只有提起是這裏需要一種用於將數據static成員定義。ODR] P2P3,這不是Foo<T>::value因爲ODR使用

  • 的左值到右值轉換立即施加到表達Foo<T>::value,和
  • 實體Foo<T>::value處於組潛在結果的表達式Foo<T>::value
  • Foo<T>::value滿足出現在常數表達式中的要求

因此在此程序中不需要定義Foo<int>::valueFoo<double>::value

但是,您將得到良好的建議總是定義靜態數據成員,就像這樣:

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

一是必須在標題,因爲模板的任何用戶可能需要進行實例化;它的重複實例將被合併。第二個必須而不是進入頭文件 - 在整個程序中只能有一個這個實體的單一定義,因爲它沒有模板化(它不是內聯函數或類或枚舉定義,它們也允許不同翻譯單元中的多個定義)。