返回转发声明的枚举类是否有效? (Visual Studio 2015链接器错误)

时间:2016-03-14 21:22:52

标签: c++ visual-studio-2015 language-lawyer c++14

我遇到一个问题,下面的代码在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的内部std库函数。

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

  

opaque-enum-declaration是对当前作用域中枚举的重新声明或新枚举的声明。 [注意:opaque-enum-declaration声明的枚举具有固定的底层类型,并且是完整类型。枚举器列表可以在稍后的重新声明中使用枚举说明符提供。 - 后注]

这段代码有效吗?如果是这样,是否有办法让VC ++接受它?

3 个答案:

答案 0 :(得分: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() {}

错误仍然存​​在;

  

错误LNK2019:未解析的外部符号“enum Code __cdecl std :: _ Invoke_ret(struct std :: _ Forced,enum Code(__ cdecl *&amp;)(void))”(?? $ _ Invoke_ret @ W4Code @@ AEAP6A?AW41 @ XZ @std @@ YA?AW4Code @@ U?$ _强制@ W4Code @@ $ 0A @@ 0 @ AEAP6A?AW41 @XZ @ Z)在函数“private:virtual enum Code __cdecl std :: _ Func_impl,enum Code&gt;”中引用。 :: _ Do_call(void)“(?_ Do_call @?$ _ Func_impl @ P6A?AW4Code @@ XZV?$ allocator @ H @ std @@ W41 @ $$ V @ std @@ EEAA?AW4Code @@ XZ)

错误提示std::function<Code()>的实例化问题,但任何一个翻译单元中的显式实例化都没有提供任何解析。

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

此时唯一的解决办法似乎是根本没有使用不透明的枚举声明,而是为其枚举器提供完整的枚举。

来自Microsoft Connect(2016-05-09);

  

此问题的修复程序已检入编译器源。修复程序应该出现在Visual C ++的未来版本中。

答案 1 :(得分:1)

以下是一些对评论来说太大的观察:

这确实是编译器错误,而不是标准库实现的错误。以下程序在不使用StdLib的情况下在VS2015 Update 1上重现相同的问题:

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)时,我们不必指定基础类型。指定基础类型时没有任何变化。

答案 2 :(得分:-2)

我认为你应该在标题(.h)中声明并在源文件(.cpp)中定义它

foo.h中

namespace Error {
    extern const Code None;
}

Foo.cpp中

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

有时枚举将被优化,并且没有实例或地址,尤其是您将其声明为静态变量。