简体   繁体   English

C ++ 11,枚举类,使用g ++的未定义引用,与clang ++一起使用

[英]C++11, enum class, undefined reference with g++, works with clang++

I used the new C++11 "enum class" type and observed a "undefined reference" problem when using g++. 我使用新的C ++ 11“枚举类”类型,并在使用g ++时观察到“未定义的引用”问题。 This probleme does not happen with clang++. clang ++不会出现这个问题。 I do not know if I am doing something wrong or if it is a g++ bug. 我不知道我做错了什么,或者它是否是一个g ++错误。

To reproduce the problem here is the code: (4 files: enum.hpp, enum.cpp, main.cpp and the Makefile) 重现这里的问题是代码:(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&);

and... 和...

// 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&);

and... 和...

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

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

and the Makefile... 和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

When I am using g++ I get: 当我使用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.

But with clang++ everything is fine (no compilation error). 但是使用clang ++一切都很好(没有编译错误)。

Any explanation is welcome, because I am lost here. 任何解释都是受欢迎的,因为我迷失在这里。

Thanks! 谢谢! :) :)


About my config: 关于我的配置:

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

The reason you are getting those errors is because g++ expects your static variables to be defined somewhere. 你得到这些错误的原因是因为g ++期望你的静态变量被定义在某个地方。

There are a couple of different ways to fix this: 有几种不同的方法可以解决这个问题:

Since you are using an integral type, you can change your structures to inherit from integral_constant. 由于您使用的是整数类型,因此可以将结构更改为继承自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>
{
};

You can also declare the variables constexpr 您还可以声明变量constexpr

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

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

You can define the static variables in your header file. 您可以在头文件中定义静态变量。

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;

This is a bug in g++. 这是g ++中的一个错误。 A definition for a static data member is required if that data member is odr-used , but the only mention of Foo<int>::value or Foo<double>::value is here: 如果该数据成员使用了odr ,则需要定义static数据成员,但是这里只提到Foo<int>::valueFoo<double>::value

switch(Foo<T>::value) {

Per [basic.def.odr]p2 and p3 , this is not an odr-use of Foo<T>::value because: Per [basic.def.odr] p2p3 ,这不是Foo<T>::value 的使用 ,因为:

  • The lvalue-to-rvalue conversion is immediately applied to the expression Foo<T>::value , and 左值到右值的转换立即应用于表达式Foo<T>::value ,和
  • The entity Foo<T>::value is in the set of potential results of the expression Foo<T>::value , and 实体Foo<T>::value在表达式Foo<T>::value 的潜在结果集合中
  • Foo<T>::value satisfies the requirements for appearing in a constant expression Foo<T>::value满足出现在常量表达式中的要求

Therefore no definition of Foo<int>::value nor Foo<double>::value is required in this program. 因此,在此程序中不需要定义Foo<int>::valueFoo<double>::value

However, you would be well-advised to always define your static data members, like so: 但是,建议您始终定义静态数据成员,如下所示:

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

The first one must go in the header, because any user of the template may need to instantiate it; 第一个必须进入标题,因为模板的任何用户可能需要实例化它; duplicate instantiations of it will be merged. 它的重复实例化将被合并。 The second one must not go in a header -- there can be only a single definition of this entity in the entire program, because it is not templated (and it is not an inline function or class or enumeration definition, which also allow multiple definitions in different translation units). 第二个不能进入标题 - 整个程序中只能有一个这个实体的定义,因为它不是模板化的(并且它不是内联函数或类或枚举定义,它也允许多个定义在不同的翻译单位)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM