Is there a way to test whether a C++ class has a default constructor (other than compiler-provided type traits)?
Sorry for answering may own question.
Googling I have found that the actual reason we can not check if a class has constructor or a destructors is that, the known technique used to detect if a class has a member is based on taking the address of the member. But constructors and destructors have no name, we can not take the address of them.
If we can not take the address, I don't see a way to make the compiler react to a construction without instantiating it directly, but in this case there is no detection at compile time but an error.
So to answer my own question, I would say that with the current techniques it is not possible to detect them and compiler support is needed. But C++ has revealed a lot of surprises, and things that were not possible at a given time, were revealed are possible using another technique.
I hope a C++ language expert is reading that and can give a more clear explanation.
Concept Traits is no more maintained, but become part of Type Traits. And in docs of has_trivial_constructor and has_trivial_destructor, Boost authors explain clearly that compiler support is required to make this work.
Warning: some analysis below is obsolete as of C++11. In C++11, access checking is done prior to instantiation and access violation is-not-an-error. Therefore the attached code may be more compliant. I haven't re-analyzed it.
I'm pretty new to SFINAE. Today it occurred to me to put a test expression inside a sizeof
inside a template parameter in a function argument type.
According to N2634 this is not wrong, but highly unportable. (EDIT: appears to be compliant to C++0x FCD.) It can only return positive or fail to compile in GCC 4.2; GCC 4.5 scores a 3 out of 3 for my test cases.
The SFINAE rules were broadened (in this case) since C++03 in the FCD. New §14.8.2/8 (emphasis mine):
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. Access checking is not done as part of the substitution process. Consequently, when deduction succeeds, an access error could still result when the function is instantiated. Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed.
template< class T >
class is_default_constructible {
template<int x>
class receive_size{};
template< class U >
static int sfinae( receive_size< sizeof U() > * );
template< class U >
static char sfinae( ... );
public:
enum { value = sizeof( sfinae<T>(0) ) == sizeof(int) };
};
class q { q(); };
class r { r(int); };
#include <iostream>
using namespace std;
int main() {
cerr << is_default_constructible<int>::value << endl // outputs 1
// fails to compile: access violation
// FCD demands that access violations be unrecoverable
// indeed, it's murky: q is default-constructible, but only "rarely"
//<< is_default_constructible<q>::value << endl
<< is_default_constructible<r>::value << endl; // outputs 0
}