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

Is It Valid To Return Forward Declared Enum Class? (Visual Studio 2015 Linker Error)

本文关键字:Studio Visual 2015 链接 错误 有效 声明 枚举 是否 返回      更新时间:2023-10-16

我有一个问题,以下代码在GCC(4.8+测试)和Clang(3.4+测试)上编译,但不能在Visual Studio 2015(VC++ 14.0)上编译。

#include <functional>
namespace Error {
enum class Code;
static const Code None = static_cast<Code>(0);
}
class Foo{
public:
  std::function<Error::Code()> Run();
};

福.cpp

#include "Foo.h"
#include <iostream>
std::function<Error::Code()> Foo::Run() {
  return [&]() {
    std::cout << "hellon"; 
    return Error::None;
  };
}

主.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 的有效代码(请参阅此答案)枚举虽然是向前声明的,但应该是一个完整的类型,可用作返回值。 以下是标准中的相关位:

不透明枚举声明是当前范围内枚举的重新声明或新枚举的声明。[注意:由不透明枚举声明声明的枚举具有固定的基础类型,并且是完整类型。枚举器列表可以在以后的重新声明中使用枚举说明符提供。—尾注 ]

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

是的,代码是有效的。

这当然似乎是MSVC错误。我能够用一个简单的代码示例重现这一点;

功能.cpp

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

主.cpp

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

错误仍然存在;

错误 LNK2019:未解析的外部符号"枚举代码__cdecl std::_Invoke_ret(struct std::_Forced,枚举代码 (__cdecl*&)(void))"(??_Invoke_ret@W4Code@@AEAP6A美元?AW41@XZ@std@@YA?AW4Code@@U?$_Forced@W4Code@@$0A@@0@AEAP6A?AW41@XZ@Z) 在函数"private: virtual enum Code __cdecl std::_Func_impl,enum Code>::_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连接 (2016-05-09);

此问题的修复程序已签入编译器源。此修复程序应显示在视觉C++的未来版本中。

以下是一些太大而无法评论的观察结果:

这确实是一个编译器错误,而不是标准库实现的错误。以下程序在VS2015 Update 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)时,我们不必指定底层类型。指定基础类型时没有任何变化。

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

福.h

namespace Error {
    extern const Code None;
}

福.cpp

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

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