8

下面的代碼使用GCC和Clang的,但不能用Visual C++:如何在Visual C++中延遲靜態數據成員的實例化?

#include <type_traits> 

struct MyType { 
    static constexpr std::size_t it = 10; 
}; 

struct MyType2 { 
}; 

template<typename T> 
struct Type2 { 
    static constexpr std::size_t it = T::it; 
}; 

int main() { 
    Type2<MyType> t1; 
    Type2<MyType2> t2; // Visual C++ complains that MyType2::it doesn't exist 
    (void) t1; 
    (void) t2; 
} 

根據標準的第14.7.1:

...初始化(和任何相關方效果 - )靜態 數據成員的不發生,除非靜態數據成員本身是在需要的靜態數據成員 的定義的方式用於存在

所以看起來這是Visual C++中的一個bug;它應該接受這個代碼。

不管怎樣,我還是希望能夠做到這一點使用Visual C++。在不改變訪問成員變量的語法的情況下,允許Visual C++工作的最簡單方法是什麼?我還要求當T::it不存在Type2<T>::it不存在,或者說,它是否則可能SFINAE斷Type2<T>::it的腦幹的。


這改變了語法,所以我不希望它:

template<typename T> 
struct Type2 { 
    template<typename = void> 
    static constexpr std::size_t it = T::it; 
}; 

// Type2<MyType>::it<> 

這是一個大量的工作,因爲我的類將包含比簡單的可變constexpr更多:

template<typename T, typename = void> 
struct Type2 { 
}; 

template<typename T> 
struct Type2<T, decltype((void) T::it)> { 
    static constexpr std::size_t it = T::it; 
}; 
+0

這似乎工作:http://coliru.stacked-crooked.com/a/1fd7999053358d43 –

+0

你過分解讀的標準。它並不是說如果從不使用成員,則允許無效的初始化器。例如。 'it =(abort(),0)'與你的例子有很大的不同,因爲它會產生運行時錯誤而不是編譯時錯誤。 –

回答

2

您可以繼承it(存在時)而不是直接聲明:

template<typename T, typename = void> 
struct ItHolder 
{}; 

template<typename T> 
struct ItHolder<T, decltype((void) T::it)> { 
    static constexpr std::size_t it = T::it; 
}; 


template<typename T> 
struct Type2 : ItHolder<T> { 
}; 

換句話說,只需要你已經提出並使用間接的另一層相結合。

+0

@Justin那麼你應該增加這個問題的要求。目前的措詞尚不清楚。 – Angew

+0

@Justin儘管解決方案仍然很容易適應。答案已更新。 – Angew

+0

這是非常好的。相比於反轉繼承(有一個'Type2Base'包含正常的實現,'Type2'幾乎和問題一樣),我更喜歡這種方式。首先,它可以很容易地擴展多個成員的實現,並且還可以更容易地將我的預期類型的​​模板參數保持爲原來的預期。 – Justin