C++ 为什么使用两个sizeofs检查一个类是否可以默认构造,而使用一个则不能?

C++ 为什么使用两个sizeofs检查一个类是否可以默认构造,而使用一个则不能?,c++,templates,metaprogramming,C++,Templates,Metaprogramming,我使用了“”中的代码 我对其进行了轻微修改,以处理所有测试用例: template< class T > class is_default_constructible { typedef int yes; typedef char no; // the second version does not work #if 1 template<int x, int y> class is_equal {}; template<

我使用了“”中的代码

我对其进行了轻微修改,以处理所有测试用例:

template< class T >
class is_default_constructible {
    typedef int yes;
    typedef char no;


    // the second version does not work
#if 1
    template<int x, int y> class is_equal {};
    template<int x> class is_equal<x,x> { typedef void type; };

    template< class U >
    static yes sfinae( typename is_equal< sizeof U(), sizeof U() >::type * );
#else
    template<int x> class is_okay { typedef void type; };

    template< class U >
    static yes sfinae( typename is_okay< sizeof U() >::type * );
#endif

    template< class U >
    static no sfinae( ... );

public:
    enum { value = sizeof( sfinae<T>(0) ) == sizeof(yes) };
};
我在这里真是不知所措:

  • 另一个版本的两个测试失败:
    int[100]
    NotDefaultConstructible
    。使用两个模板参数版本,所有测试都成功
  • Visual Studio 2010不支持
    std::is_default_constructible
    。然而,我的问题是,为什么这两种实现有任何不同,为什么一种可以工作而另一种不能

  • 这几乎可以肯定是编译器的一个工件(bug),因为g++的行为(和失败)不同。我只能猜测为什么VS的行为不同,但有一个猜测似乎是合理的,那就是这个类:

    template<int x> class is_okay { typedef void type; };
    
    模板类是_ok{typedef void type;};
    
    无论模板参数如何,都具有相同的定义,因此编译器在分析
    静态sfinae(typename是_ok::type*)时可能会跳过一步
    并认为它定义得很好,而不仔细查看
    的参数是否正常
    。所以它认为一切都是可以默认构造的

    我不知道为什么VS和g++都不介意
    是不是可以::type
    是私有的。看来他们两个都应该这样

    另一方面,g++将这两个版本视为等效版本。但是,在这两种情况下,
    int[100]
    会产生不同的错误。关于它是否应该是默认可构造的,这一点是有争议的。你似乎认为不应该这样。g++47的
    std::is_default_constructible
    认为它是可构造的!要获得该行为(可能更标准),可以在
    enum
    行中将
    T
    替换为
    typename boost::remove_all_extends::type

    (我的答案从DS之前的答案中得到了很大的启示。)

    首先,请注意,您有
    类is_ok{typedef void type;}
    ,即
    类型
    is_ok
    的私有成员。这意味着它在类之外实际上是不可见的,因此

    template< class U >
    static yes sfinae( typename is_equal< sizeof U(), sizeof U() >::type * );
    

    为什么不检查一下标准库中是否已经有了它?什么不起作用?它在g++上运行良好,除了
    BOOST\u STATIC\u ASSERT(!is\u default\u constructible::value)@B如果一个断言失败,并且该断言是正确的,那么它肯定不能正常工作?g++-4.7.1正确编译这两个变体。您应该使用
    typedef char yes[1];typedef字符编号[2]
    以保证它们的大小不同(
    int
    char
    理论上可以是相同的大小);而且我觉得它更容易阅读。你已经很好地解释了这一点。我将其设置为失败
    int[100]
    ,因为我编写了一个模板函数,其中包含一个可选参数
    T defaultValue=T()
    ,该参数在VS2010中不适用于
    T=int[100]
    。直到现在我才知道默认值与值的初始化。
    template< class U >
    static yes sfinae( typename is_equal< sizeof U(), sizeof U() >::type * );
    
    template< class T >
    class is_default_constructible {
        typedef int yes;
        typedef char no;
    
        template<int x> struct is_okay { typedef int type; };
    
        template< class U >
        static yes sfinae( typename is_okay< sizeof (*new U) >::type );
    
        template< class U >
        static no sfinae( ... );
    
    public:
        enum { value = sizeof( sfinae<T>(0) ) == sizeof(yes) };
    };
    
    #if __has_feature(cxx_static_assert)
    #define BOOST_STATIC_ASSERT(x) static_assert(x, "or fail")
    #else
    #define dummy2(line) dummy ## line
    #define dummy(line) dummy2(line)
    #define BOOST_STATIC_ASSERT(x) int dummy(__COUNTER__)[(x) - 1]
    #endif
    
    #include <string>
    
    BOOST_STATIC_ASSERT( !is_default_constructible<int()>::value );
    BOOST_STATIC_ASSERT( is_default_constructible<bool>::value );
    BOOST_STATIC_ASSERT( is_default_constructible<std::string>::value );
    BOOST_STATIC_ASSERT( is_default_constructible<int[100]>::value );
    
    BOOST_STATIC_ASSERT( is_default_constructible<const std::string>::value );
    
    struct NotDefaultConstructible {
        const int x;
        NotDefaultConstructible( int a ) : x(a) {}
    };
    
    BOOST_STATIC_ASSERT( !is_default_constructible<NotDefaultConstructible>::value );
    
    struct DefaultConstructible {
        const int x;
    
        DefaultConstructible() : x(0) {}
    };
    
    BOOST_STATIC_ASSERT( is_default_constructible<DefaultConstructible>::value );