簡體   English   中英

返回轉發聲明的枚舉類是否有效? (Visual Studio 2015鏈接器錯誤)

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

我有一個問題,下面的代碼在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庫std::function

此代碼類似於我嘗試使用的內部庫的使用,它共享工具的標准程序接口,但轉發聲明錯誤代碼,以便可以自定義它們。 我認為這應該是基於§7.2的有效代碼(請參閱此答案 )枚舉雖然是前向聲明的,但應該是一個完整的類型並可用作返回值。 以下是標准中的相關位:

opaque-enum-declaration是當前作用域中枚舉的重新聲明或新枚舉的聲明。 [注意:opaque-enum-declaration聲明的枚舉具有固定的底層類型,並且是完整類型。 枚舉器列表可以在稍后的重新聲明中使用枚舉說明符提供。 - 尾注]

這段代碼有效嗎? 如果是這樣,是否有辦法讓VC ++接受它?

是的,代碼有效。

這肯定似乎是MSVC的bug。 我可以使用簡單的代碼示例重現這一點;

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:未解析的外部符號“枚舉代碼__cdecl std :: _ Invoke_ret(struct std :: _ Forced,enum Code(__ cdecl *&)(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> :: _ Do_call(無效)“(?_ 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 ++的未來版本中。

以下是一些對評論來說太大的觀察:

這確實是編譯器錯誤,而不是標准庫實現的錯誤。 以下程序在不使用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 )。
  • 顯式指定基礎類型時問題仍然存在。
  • 如果我們用unscoped枚舉替換范圍枚舉,問題仍然存在。 使用Microsoft的C ++擴展( /Ze )時,我們不必指定基礎類型。 指定基礎類型時沒有任何變化。

我認為你應該在頭文件(.h)中聲明None並在源文件(.cpp)中定義它

foo.h中

namespace Error {
    extern const Code None;
}

Foo.cpp中

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

有時枚舉將被優化,並且沒有實例或地址,尤其是您將其聲明為靜態變量。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM