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