【发布时间】:2016-06-30 03:06:23
【问题描述】:
我遇到以下代码在 GCC(4.8+ 测试)和 Clang(3.4+ 测试)上编译但在 Visual Studio 2015 (VC++ 14.0) 上无法编译的问题。
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> &)" (??$_Invoke_ret@W4Code@Error@@AAV<lambda_813e82254384ef384f6a5fe34e885f01>@@@std@@YA?AW4Code@Error@@U?$_Forced@W4Code@Error@@$0A@@0@AAV<lambda_813e82254384ef384f6a5fe34e885f01>@@@Z)
我认为这是一个用于实现 std::function 的内部标准库函数。
此代码类似于我尝试使用的内部库的使用,它共享工具的标准程序接口,但前向声明错误代码以便可以自定义它们。我相信这应该是基于 §7.2 的有效代码(请参阅this answer)枚举虽然是前向声明的,但应该是一个完整的类型并且可用作返回值。这是标准中的相关位:
一个 opaque-enum-declaration 要么是在当前范围内重新声明一个枚举,要么是一个新枚举的声明。 [注意:由 opaque-enum-declaration 声明的枚举具有固定的基础类型并且是完整的类型。枚举器列表可以在稍后的重新声明中使用枚举说明符提供。 ——尾注]
此代码有效吗?如果是这样,是否有办法让 VC++ 接受它?
【问题讨论】:
-
“枚举虽然是前向声明的,但应该是一个完整的类型并且可以用作返回值。”前向声明不提供完整的类型 枚举与否。
-
@πάνταῥεῖ 这就是为什么这在技术上不是一个前向声明而是一个不透明的枚举声明(如果“前向声明”这个术语有一个正确的定义......)无论如何,这样的声明引入了一个完整的枚举类型。枚举有一个特殊情况,IIRC 永远不会引入不完整的枚举类型。
-
您的
enum class Code定义 与您的声明的enum class Error::Code不在同一个命名空间中 -
我认为从您的引用中可以清楚地看出代码是正确的,因此它一定是一个 VS 错误。建议提交 VS 错误报告并删除 language-lawyer 标签
-
我刚刚在 VS2015U1 上解决了这个问题。如果您使用正确的类而不是 lambda,它仍然会出现,明确指定 lambda 上的返回类型,使用
return {}而不是return Error::None,将所有内容移动到一个源文件中。正如 Sam Cristall 所注意到的那样,关键点是缺少枚举的定义(即使是没有枚举数的定义也足够了)。一旦该定义存在,就不会发生链接器问题。我同意@M.M 这是一个 VS 错误。
标签: c++ visual-studio-2015 language-lawyer c++14