C ++ 11, enum класс, неопределенная ссылка с g ++, работает с clang ++

Я использовал новый тип enum класса 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);
}

и 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

Когда я использую 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

Решение

Причина, по которой вы получаете эти ошибки, в том, что g ++ ожидает, что ваши статические переменные будут где-то определены.

Есть несколько способов исправить это:

Поскольку вы используете целочисленный тип, вы можете изменить свои структуры так, чтобы они наследовали от integra_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;
2

Другие решения

Это ошибка в g ++. Определение для static элемент данных требуется, если этот элемент данных УСО используемый, но единственное упоминание о Foo<int>::value или же Foo<double>::value это здесь:

switch(Foo<T>::value) {

в [Basic.def.odr] р2 а также p3, это не УСО использование из Foo<T>::value так как:

  • Преобразование lvalue-to-rvalue немедленно применяется к выражению Foo<T>::value, а также
  • Лицо Foo<T>::value находится в множество потенциальных результатов выражения Foo<T>::value, а также
  • Foo<T>::value удовлетворяет требованиям для появления в постоянном выражении

Поэтому нет определения Foo<int>::value ни 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;

Первый должен идти в заголовке, потому что любой пользователь шаблона может нуждаться в его создании; повторяющиеся экземпляры этого будут объединены. Второй должен не перейти в заголовок — во всей программе может быть только одно определение этой сущности, потому что оно не шаблонизировано (и это не встроенная функция или определение класса или перечисления, которые также допускают несколько определений в разных единицах перевода) ,

1

По вопросам рекламы [email protected]