7

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?

user541686
  • 205,094
  • 128
  • 528
  • 886
  • By the way, I wouldn't be surprised if this was a duplicate, so please close if it is. It's not easy to search for though. – user541686 Aug 13 '14 at 10:22
  • 2
    Please always specify the version when you say: with gcc, with clang, etc. – Marc Glisse Aug 13 '14 at 11:40
  • @MarcGlisse: I didn't see why it might matter, but it's Clang 3.6.0 and GCC 4.8.1. – user541686 Aug 13 '14 at 16:23
  • @Mehrdad: It matters quite a bit considering that whether the visibility is considered in the SFINAE context or not has changed in in C++11. Compilers that predate that (or follow the old rules, including g++4.4) will fail due to the accesibility issue, while compilers that use the C++11 rules won't consider it. – David Rodríguez - dribeas Aug 13 '14 at 17:18
  • @David: But I thought I already said I'm doing this in C++03 mode... shouldn't that be enough? – user541686 Aug 13 '14 at 17:46
  • @Mehrdad: we can discuss this as much as you want, but I can provide two different versions of gcc that will accept/reject the code. Does the version matter? Obviously, does it not? Ok, maybe not for the overaching question of which compiler is right in C++03... the code is invalid in C++03 – David Rodríguez - dribeas Aug 13 '14 at 17:48
  • Hmm. According to [N2634](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html), as late as 2008, access checking was *not* performed during substitution. I wonder when that changed.. – dyp Aug 13 '14 at 18:07

1 Answers1

0

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.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Either you mixed up the compilers or I'm not understanding your answer, because clearly VC++ doesn't think the code is invalid... – user541686 Aug 14 '14 at 05:43
  • 1
    @Mehrdad: In VS you cannot control whether it builds in C++03 or C++11 mode, it has only one mode. In C++03 the code is invalid (hard error), in C++11 access checking is done as part of the substitution process, SFINAE should kick in and pick the ellipsis overload. Summarizing: clang++ is correctly following the C++03 rules, g++ is following the C++11 rules (even with `-std=c++03`!), VS is not following C++03 nor C++11 rules correctly. – David Rodríguez - dribeas Aug 14 '14 at 13:45
  • Yeah I understand that, what confused me was that you said the "other compilers" are abiding by C++03 which didn't make any sense but I see you fixed it now. – user541686 Aug 14 '14 at 18:58