2016-10-02 112 views
3

考虑这个例子:检查类型被定义

#include <iostream> 
#include <type_traits> 

template <class, class = void> 
struct is_defined : std::false_type 
{ }; 

template <class T> 
struct is_defined<T, 
    std::enable_if_t<std::is_object<T>::value && 
        !std::is_pointer<T>::value 
     > 
    > : std::true_type 
{ 
private: 
    static const T test; //try to create incomplete type member 
}; 

struct defined { }; 

struct forward_declared; 

int main() 
{ 
    std::cout << std::boolalpha 
       << is_defined<defined>::value << std::endl 
       << is_defined<forward_declared>::value << std::endl; 
} 

输出是true两者。我想如果我尝试使不完整类型的成员struct成员,那么这个模板专门化将从超载设置中丢弃。但事实并非如此。删除static const会导致不完整的编译时错误。这种方法有什么问题,如果可能的话,怎么能实现呢?

+0

'is_object'并不意味着'is_complete'。 –

+0

@ n.m。这只是为了不允许检查参考。我可以使用'std :: is_reference'来代替,但这并没有太大的不同。 – xinaiz

+0

好的我看到你正在试图声明一个静态的东西去除不完整的类型。无论如何,你需要使用ODR来使用静态的东西。 –

回答

6

试试这个:

template <class T> 
struct is_defined<T, 
    std::enable_if_t<std::is_object<T>::value && 
        !std::is_pointer<T>::value && 
        (sizeof(T) > 0) 
     > 
    > : std::true_type 
{ 
}; 
+0

哇,这是快速的:)你能解释为什么我们可以通过尝试在不完整类型上应用'sizeof'来放弃超载,但是我们不能通过使'T'成员来做到这一点? – xinaiz

+0

@BlackMoses这只是你的普通的SFINAE。 'sizeof'不适用于不完整的类型,所以替换将失败。 –

1

关于问题“什么是不对的做法......” 当我们有静态常量T检验;它不是is_defined < ...>类的一部分,我们实际上必须定义静态成员is_defined < ...> ::测试别的地方,只有在那个“其他”地方,我们需要T是完整类型。 is_defined < ...>仅仅因为静态成员大小对is_defined < ...>大小没有影响才编译类。

去除静态const会导致T test;成为is_defined < ...>(并且影响is_defined < ...> size)的成员,因此T必须是完整类型才能具有已知大小。

UPD注:在声明类构件在模板特它不是由SFINAE丢弃仅仅因为这是也不是“参数替换”,也不是一种“SFINAE表达”的情况下(以及不是函数超载!)。相反,它是某种“SFINAE现场声明” - 而不是标准中所述的“替代失败时不是错误”。

+0

感谢您的解释。但是,当这种不完整类型是成员时,为什么模板专门化不会从过载中丢弃?相反,它会导致错误。 – xinaiz

+0

在模板特化中声明类成员的情况下,它不会被SFINAE丢弃,因为这不是“参数替换”,也不是“SFINAE表达式”(也不是被重载的函数!)。相反,它是某种“SFINAE现场声明” - 而不是标准中所述的“替代失败时不是错误”。 –

1

一般来说,在这种情况下,您可以使用您的sfinae表达式中的某些不接受不完整类型的运算符。
举个例子,你可以使用typeid

#include <iostream> 
#include <type_traits> 
#include <utility> 

template<typename T, typename = void> 
constexpr bool is_defined = false; 

template<typename T> 
constexpr bool is_defined<T, decltype(typeid(T), void())> = true; 

struct defined { }; 
struct forward_declared; 

int main() 
{ 
    std::cout << std::boolalpha 
       << is_defined<defined> << std::endl 
       << is_defined<forward_declared> << std::endl; 
} 

正如其他人所提到的,另一个有效运营商sizeof