Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++11

c++14

I need to check if a class C has a default constructor, either implicit or custom, and either public, protected or private.

I tried using std::is_default_constructible<C>::value, which returns true if C has a public default constructor (either implicit or custom) but false if C has a protected or private default constructor (seams to be the standard behavior though.)

Is there any way to check if a class has a protected or private default constructor ?

Note (if this may help): the check is performed from a function that is friend of the class C to be checked.


I need to perform this check in order to default-construct objects corresponding to the nullptr pointers of the m_objs tuple, member of a Foo object (partial Foo definition below):

template<class... Objects>
class Foo
{
public:
    Foo(Objects*... objects)
    : m_objs(objects...)
    {
        // User construct a Foo objects passing a pack of pointers
        // some of them are nullptr, some are not.
        // The following call should default-construct objects corresponding
        // to the null pointers of m_objs member tuple:
        objs_ctor<Objects...>();
    }
private:
    template<class Obj, class O1, class ...On>
    void objs_ctor()
    {
        objs_ctor<Obj>(); objs_ctor<O1, On...>();
    }
    template<class Obj>
    typename std::enable_if<std::is_default_constructible<Obj>::value, void>::type
    objs_ctor()
    {
        Obj*& obj = std::get<Obj*>(m_objs);
        if (obj == nullptr)
            obj = new Obj;    // default-construct Obj
    }
    template<class Obj>
    typename std::enable_if<!std::is_default_constructible<Obj>::value, void>::type
    objs_ctor()
    {
        Obj*& obj = std::get<Obj*>(m_objs);
        assert(obj != nullptr);   // terminate if not pre-constructed
    }

private:
    std::tuple<Objects*...>     m_objs;
};

which is intended to be used as:

struct A { };
class B {
    B() = default;

    template <class... Ts>
    friend class Foo;
};

int main() {
    // currently asserts, even though Foo<A,B> is a friend of B
    // and B is default-constructible to its friends
    Foo<A, B> foo(nullptr, nullptr);
}

Example above asserts because std::is_default_constructible<B>::value is false, even though B has a [private] default ctor and Foo<A,B> is friend of B.

like image 976
shrike Avatar asked May 13 '16 13:05

shrike


1 Answers

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;
}
like image 119
linuxfever Avatar answered Oct 06 '22 03:10

linuxfever