是否有效返回声明的枚举类? (Visual Studio 2015链接器错误)
我遇到以下代码在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++接受它?
是的,代码是有效的。
这当然确实看起来是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++.
谢谢。我发现,作为解决方案,您也可以为翻译单元提供一个“错误的”枚举定义,只能看到不透明的声明。这显然是一个黑客。 –
@SamCristall:这不是破解,这是一个ODR违规。即,这不是一种解决方法,它是UB。 – ildjarn
@ildjarn我不认为hack是一个非常明确的术语!但是,谢谢你的警告。它似乎正在为我所需要的工作,幸运的是不是生产代码。 –
我想你应该申报无在头(.h)和源文件(的.cpp)
foo.h中定义它
namespace Error {
extern const Code None;
}
Foo.cpp中
namespace Error {
const Code None = static_cast<Code>(0);
}
有时枚举将被优化,将没有实例或地址,特别是你声明它为一个静态变量。
这里有一些更多的意见太大评论:
这确实是一个编译器缺陷,不是标准库实现的一个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
)。指定基础类型时没有任何变化。
_“枚举虽然是前向声明的,应该是一个完整的类型并且可用作返回值。”_前向声明不提供_complete types_ enum或不。 –
@πάνταῥεῖ这就是为什么这在技术上不是一个前向声明,而是一个不透明枚举声明(如果甚至对“前向声明”这个术语甚至有一个恰当的定义......)无论如何,这样的声明引入了* a完全枚举类型*。枚举有一个特殊的情况,IIRC永远不会引入不完全的枚举类型。 – dyp
你的'enum class Code' _definition_与你的_declared_'enum类不在同一个命名空间中Error :: Code' – inetknght