I used the code from “Is there a way to test whether a C++ class has a default constructor (other than compiler-provided type traits)?“.
I modified it slightly to work with all my test cases:
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) };
};
Why does it work correctly with the two template argument version but not with the normal one (set #if 0)?
Is this a compiler bug? I’m using Visual Studio 2010.
I used the following tests:
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 );
I’m really at a loss here:
- Two tests are failing with the other version:
int[100]andNotDefaultConstructible. All the tests succeed with the two template argument version. - Visual Studio 2010 does not support
std::is_default_constructible. However, my question is about why there is any difference in the two implementations and why one works and the other does not.
(My answer is greatly informed by DS’s previous answer.)
First of all, notice that you have
class is_okay { typedef void type; }, i.e.,typeis a private member ofis_okay. This means that it’s not actually visible outside the class and thereforewill never succeed. However, SFINAE didn’t originally apply to this situation in C++98; it wasn’t until the resolution to DR 1170 that “access checking [started being] done as part of the substitution process”.[1]
Clang (top-of-tree) follows the DR in
-std=c++11mode, but gives the expected error in its default C++03 mode (i.e. Clang doesn’t follow the DR in C++03 mode). This is slightly odd but maybe they do it for backwards compatibility.But anyway, you don’t actually want
typeto be private in the first place. What you meant to write isstruct is_equalandstruct is_okay.With this change, Clang passes all of your test cases. GCC 4.6.1 passes all of your test cases too, except for
int[100]. GCC thinks thatint[100]is okay, whereas you’re asserting that it’s not okay.But another problem with your code is that it doesn’t test what you think it’s testing. The C++ standard, clause 8.5#10, says very clearly: [2]
So when you write
sizeof U(), you’re not testing ifUcan be default-initialized; you’re testing if it can be value-initialized!…Or are you? At least in some of my test cases, GCC’s error messages indicated that
U()was being interpreted as the name of a type — “function returningU” — and that was whyint[100]behaved differently. I don’t see how that behavior is valid, but I really don’t understand the syntactic subtleties here.If you really mean to test default initialization, you should use something like
sizeof *new Ueverywhere you currently havesizeof U().By the way,
int[100]is default-initializable, period. The Standard is clear on what it means to default-initialize an array type.Finally, I wonder if one cause of wacky behavior in your code is that you’re trying to pass an unadorned
0(which is of typeint) to a function whose set of overloads includes one function takingvoid *and one taking.... I could totally understand if a compiler picked the wrong one in that case. You’d be better advised to try passing0to a function takingint.Putting it all together, here’s a version of your code that works perfectly for me (i.e., no assertion-failures) in both ToT Clang and GCC 4.6.1.