2016-03-14 36 views
3

我遇到以下代码在GCC(4.8+测试)和Clang(3.4+测试)编译但未在Visual Studio 2015(VC++ 14.0)上编译的问题。是否有效返回声明的枚举类? (Visual Studio 2015链接器错误)

foo.h中:

#include <functional> 

namespace Error { 
enum class Code; 
static const Code None = static_cast<Code>(0); 
} 

class Foo{ 
public: 
    std::function<Error::Code()> Run(); 
}; 

Foo.cpp中

#include "Foo.h" 
#include <iostream> 

std::function<Error::Code()> Foo::Run() { 
    return [&]() { 
    std::cout << "hello\n"; 
    return Error::None; 
    }; 
} 

main.cpp中:

#include "Foo.h" 

namespace Error { 
enum class Code { 
    None = 0, 
    Error = 1, 
}; 
} 

int main() { 
    Foo foo; 
    foo.Run()(); 
} 

在VC 14.0将得到的误差如下:

Foo.obj : error LNK2001: unresolved external symbol "enum Error::Code __cdecl std::_Invoke_ret<enum Error::Code,class <lambda_813e82254384ef384f6a5fe34e885f01> &>(struct std::_Forced<enum Error::Code,0>,class <lambda_813e82254384ef384f6a5fe34e885f01> &)" ([email protected]@[email protected]@AAV<lambda_813e82254384ef384f6a5fe34e885f01>@@@[email protected]@[email protected]@@[email protected]@[email protected]@[email protected]@[email protected]<lambda_813e82254384ef384f6a5fe34e885f01>@@@Z) 

我认为这是一个内部std库函数,用于实现std::function

此代码类似于内部库的用法我试图使用它共享工具的标准程序接口,但前进声明错误代码,以便可以自定义它们。我相信这应该是基于§7.2的有效代码(参见this answer)虽然前面声明的枚举应该是一个完整类型,并且可用作返回值。以下是标准中的相关位:

An opaque-enum-declaration is either a redeclaration of an enumeration in the current scope or a declaration of a new enumeration. [Note: An enumeration declared by an opaque-enum-declaration has fixed underlying type and is a complete type. The list of enumerators can be provided in a later redeclaration with an enum-specifier. —end note ]

此代码是否有效?如果是这样,是否有解决方法让VC++接受它?

+4

_“枚举虽然是前向声明的,应该是一个完整的类型并且可用作返回值。”_前向声明不提供_complete types_ enum或不。 –

+5

@πάνταῥεῖ这就是为什么这在技术上不是一个前向声明,而是一个不透明枚举声明(如果甚至对“前向声明”这个术语甚至有一个恰当的定义......)无论如何,这样的声明引入了* a完全枚举类型*。枚举有一个特殊的情况,IIRC永远不会引入不完全的枚举类型。 – dyp

+0

你的'enum class Code' _definition_与你的_declared_'enum类不在同一个命名空间中Error :: Code' – inetknght

回答

3

是的,代码是有效的。

这当然确实看起来是MSVC的错误。我可以用一个简单的代码示例重现这一点;

func.cpp

#include <functional> 
enum Code : int; 
Code func2(); 
void func() 
{ 
    std::function<Code()> f2 { func2 }; 
} 

的main.cpp

enum Code : int { 
    Some = 0, 
    Error = 1, 
}; 
Code func2() { return Some; } 
int main() {} 

错误仍存在;

error LNK2019: unresolved external symbol "enum Code __cdecl std::_Invoke_ret(struct std::_Forced,enum Code (__cdecl*&)(void))" ([email protected]@@[email protected]@[email protected]@[email protected]@[email protected]@@[email protected]@[email protected][email protected]@Z) referenced in function "private: virtual enum Code __cdecl std::_Func_impl,enum Code>::_Do_call(void)" ([email protected][email protected][email protected]@[email protected]@[email protected]@[email protected][email protected]@@[email protected]@XZ)

错误暗示与std::function<Code()>实例化的问题,但没有在任何翻译单元显式实例的数量提供任何决议。

该错误似乎也不依赖于未调用的enum与范围为enum class

此时唯一的解决方法是出现根本不使用不透明的枚举声明,而是使用枚举器提供完整的枚举。


From Microsoft Connect(2016-05-09);

A fix for this issue has been checked into the compiler sources. The fix should show up in the future release of Visual C++.

+1

谢谢。我发现,作为解决方案,您也可以为翻译单元提供一个“错误的”枚举定义,只能看到不透明的声明。这显然是一个黑客。 –

+0

@SamCristall:这不是破解,这是一个ODR违规。即,这不是一种解决方法,它是UB。 – ildjarn

+0

@ildjarn我不认为hack是一个非常明确的术语!但是,谢谢你的警告。它似乎正在为我所需要的工作,幸运的是不是生产代码。 –

-2

我想你应该申报在头(.h)和源文件(的.cpp)

foo.h中定义它

namespace Error { 
    extern const Code None; 
} 

Foo.cpp中

namespace Error { 
    const Code None = static_cast<Code>(0); 
} 

有时枚举将被优化,将没有实例或地址,特别是你声明它为一个静态变量。

1

这里有一些更多的意见太大评论:

这确实是一个编译器缺陷,不是标准库实现的一个bug。下面的程序再现上VS2015更新1相同的问题,而使用STDLIB:

template<class T> 
T create() { 
    return {}; 
} 

enum class Code; 

int main() { 
    create<Code>(); 
} 

链接器抱怨一个未解决的符号:

enum Code __cdecl create<enum Code>(void)

  • 链接器的问题,如果你离开了消失返回值(将T替换为void)。
  • 显式指定基础类型时,问题仍然存在。
  • 如果我们用非范围枚举替换范围枚举,问题仍然存在。使用Microsoft的C++扩展时,我们不必指定基础类型(/Ze)。指定基础类型时没有任何变化。