How to Test Whether a C++ Class Has a Default Constructor (Other Than Compiler-Provided Type Traits)

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.

How to check if a class has a default constructor, either public, protected or private

I will present a simplified example to make things easier. Then you can adapt it to your Foos class. The idea is to specialise my templated friend class as follows

#include <iostream>    

// forward declaration
template <class... T>
struct Friend;

struct testing_tag;

// specialisation simply to check if default constructible
template <class T>
struct Friend<T, testing_tag> {

// sfinae trick has to be nested in the Friend class
// this candidate will be ignored if X does not have a default constructor
template <class X, class = decltype(X())>
static std::true_type test(X*);

template <class X>
static std::false_type test(...);

static constexpr bool value = decltype(test<T>(0))::value;
};

Notice that the std::true_type candidate will always be valid as long as X has a default constructor regardless if it is private, protected or public. Now test it

class default_public {

template <class... T>
friend struct Friend;

};

class default_protected {

template <class... T>
friend struct Friend;

protected:
default_protected() {}
};

class default_private {

template <class... T>
friend struct Friend;

private:
default_private() {}

};

class no_default {

public:
no_default(int x){}
};

// a convenient name to test with
template <class T>
using has_any_default_constructor = Friend<T, testing_tag>;

int main() {
std::cout << has_any_default_constructor<default_public>::value << std::endl;
std::cout << has_any_default_constructor<default_protected>::value << std::endl;
std::cout << has_any_default_constructor<default_private>::value << std::endl;
std::cout << has_any_default_constructor<no_default>::value << std::endl;
}

Why does using two sizeofs work to check whether a class is default constructible, but one 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., type is a private member of is_okay. This means that it's not actually visible outside the class and therefore

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

will 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]

(Amazingly, Paolo Carlini wrote that blog entry just 10 days ago, so your timing with this question is impeccable. In cases like this, according to Carlini, GCC prior to 4.8 didn't do access checking during SFINAE at all. So that explains why you didn't see GCC complaining about the private-ness of type. You'd have to be using a top-of-tree GCC from literally less than two weeks ago, in order to see the correct behavior.)

Clang (top-of-tree) follows the DR in -std=c++11 mode, 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 type to be private in the first place. What you meant to write is struct is_equal and struct 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 that int[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]

An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

So when you write sizeof U(), you're not testing if U can 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 returning U" — and that was why int[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 U everywhere you currently have sizeof 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 type int) to a function whose set of overloads includes one function taking void * and one taking .... I could totally understand if a compiler picked the wrong one in that case. You'd be better advised to try passing 0 to a function taking int.

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.

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 );

Detect if a default constructor exists at compile time

Here's a possible implementation of the type trait:

template<typename T>
class is_default_constructible {

typedef char yes;
typedef struct { char arr[2]; } no;

template<typename U>
static decltype(U(), yes()) test(int);

template<typename>
static no test(...);

public:

static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

struct foo {
foo(int) {}
};

int main()
{
std::cout << is_default_constructible<foo>::value << '\n' // 0
<< is_default_constructible<std::vector<int>>::value; // 1
}

Is there a way to test that a default constructor does not exist?

You can use static_assert(!std::is_default_constructible<T>::value, "Boo");. Make sure to #include <type_traits>.

Detect if class has constructor with signature

How can I extend this to the check I want to perform?

You could use decltype(...) on an T{...} expression as follows:

struct Foo 
{
Foo(Archive&) { }
};

struct Bar
{
// unsupported
};

template<class T>
using HasUnarchiveConstructorImpl =
decltype(T{std::declval<Archive&>()});

template <class T>
using HasUnarchiveConstructor =
std::experimental::is_detected<HasUnarchiveConstructorImpl, T>;

static_assert(HasUnarchiveConstructor<Foo>::value);
static_assert(!HasUnarchiveConstructor<Bar>::value);

live example on wandbox


Or how can I do it in a different way?

See Howard Hinnant's answer.

Detect compiler generated default constructor using reflection in C#

There is no way to detect automatically generated default constructors through metadata. You can test this by creating a class library with two classes, one with an explicit default constructor, and one without. Then run ildasm on the assembly: the metadata of the two constructors is identical.

Rather than try to detect generated constructors, I would simply change the program to allow missing documentation on any default constructor. Most documentation generation programs, like NDoc and SandcastleGUI, have an option to add standard documentation to all default constructors; so it's really not necessary to document them at all. If you have an explicit default constructor in your code, you can put three slashes (///) above the constructor - nothing else - to disable the Visual Studio warning about missing documentation.



Related Topics



Leave a reply



Submit