2016-03-14 34 views
3

我遇到以下代碼在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++接受它?

+4

_「枚舉雖然是前向聲明的,應該是一個完整的類型並且可用作返回值。」_前向聲明不提供_complete types_ enum或不。 –

+5

@πάνταῥεῖ這就是爲什麼這在技術上不是一個前向聲明,而是一個不透明枚舉聲明(如果甚至對「前向聲明」這個術語甚至有一個恰當的定義......)無論如何,這樣的聲明引入了* a完全枚舉類型*。枚舉有一個特殊的情況,IIRC永遠不會引入不完全的枚舉類型。 – dyp

+0

你的'enum class Code' _definition_與你的_declared_'enum類不在同一個命名空間中Error :: Code' – inetknght

回答

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() {} 

錯誤仍存在;

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++.

+1

謝謝。我發現,作爲解決方案,您也可以爲翻譯單元提供一個「錯誤的」枚舉定義,只能看到不透明的聲明。這顯然是一個黑客。 –

+0

@SamCristall:這不是破解,這是一個ODR違規。即,這不是一種解決方法,它是UB。 – ildjarn

+0

@ildjarn我不認爲hack是一個非常明確的術語!但是,謝謝你的警告。它似乎正在爲我所需要的工作,幸運的是不是生產代碼。 –

-2

我想你應該申報在頭(.h)和源文件(的.cpp)

foo.h中定義它

namespace Error { 
    extern const Code None; 
} 

Foo.cpp中

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

有時枚舉將被優化,將沒有實例或地址,特別是你聲明它爲一個靜態變量。

1

這裏有一些更多的意見太大評論:

這確實是一個編譯器缺陷,不是標準庫實現的一個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)。指定基礎類型時沒有任何變化。