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);
}

和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

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++ 中的一个错误。static如果该数据成员是odr-used ,则需要该数据成员的定义,但这里唯一提到Foo<int>::valueorFoo<double>::value是:

switch(Foo<T>::value) {

根据[basic.def.odr]p2p3,这不是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;

第一个必须放在标题中,因为模板的任何用户都可能需要实例化它;它的重复实例将被合并。第二个不能放在头文件中——在整个程序中这个实体只能有一个定义,因为它不是模板化的(而且它不是内联函数或类或枚举定义,它也允许多个定义在不同的翻译单元中)。

于 2013-05-20T06:36:34.817 回答