Search code examples
c++template-meta-programmingsfinaec++03access-specifier

SFINAE and visibility-checking in Clang vs. GCC vs. MSVC -- which is correct?


I've written my attempt at a C++03-compatible implementation of is_default_constructible:

template<class = void> struct is_default_constructible;
template<> struct is_default_constructible<>
{
protected:
    // Put base typedefs here to avoid pollution
    struct twoc { char a, b; };
    template<bool> struct test { typedef char type; };
    template<class T> static T declval();
};
template<> struct is_default_constructible<>::test<true> { typedef twoc type; };
template<class T> struct is_default_constructible : is_default_constructible<>
{
private:
    template<class U> static typename test<!!sizeof(::new U())>::type sfinae(U*);
    template<class U> static char sfinae(...);
public:
    static bool const value = sizeof(sfinae<T>(0)) > 1;
};

When I test it in GCC (-std=c++03), it returns 0 because the constructor is invisible:

class Test { Test(); };

int main()
{
    return is_default_constructible<Test>::value;
}

When I test it in Visual C++ (different versions all have the same behavior), I get back 1.

And when I test it in Clang (also -std=c++03), I get:

error: calling a private constructor of class 'Test'
template<class U> static typename test<!!sizeof(::new U())>::type sfinae(U *);
                                                      ^
note: while substituting explicitly-specified template arguments into function template 'sfinae' 
static bool const value = sizeof(sfinae<T>(0)) > 1;
                                 ^
note: in instantiation of template class 'is_default_constructible<Test>' requested here
return is_default_constructible<Test>::value;
       ^
error: calling a private constructor of class 'Test'
template<class U> static typename test<!!sizeof(::new U())>::type sfinae(U *);
                                                      ^
note: while substituting deduced template arguments into function template 'sfinae' [with U = Test]
static bool const value = sizeof(sfinae<T>(0)) > 1;
                                 ^
note: in instantiation of template class 'is_default_constructible<Test>' requested here
return is_default_constructible<Test>::value;

Which compiler is correct and why?


Solution

  • The code is not valid C++03, although it is valid C++11. The g++ 4.8 compiler is abiding to the C++11 rules and ignoring inaccessible members in an SFINAE context, while clang compilers is abiding to the C++03 where the member (constructor in this case) is found and selected, but access checks make the code invalid. VS (whatever version you are using) is not abiding to C++11 or C++03 rules, it seems to be ignoring the access specifier inside the sizeof completely.