3

How can I compile-time detect if a class has a constructor with a given signature? To be specific I would like to do the following:

class Archive
{
    // (...)

    template <typename T>
    T Read()
    {
        if constexpr(HasUnarchiveConstructor<T>())
        {
            return T(*this); // constructor accepts reference to Factory
        }
        else
        {
            T t;
            Unarchive(*this, t); // use stand alone function instead. (if this is not available either, compiling fails)
            return t;
        }
    }
}

There are many sources for detecting functions with certain signatures. I am however unable to convert these to constructors. source 1 source 2 source 3, and more.

From sources I found I compiled the following to detect if a function has the plus operator:

template<typename T>
using HasUnarchiveConstructorImpl = decltype(std::declval<T>() + std::declval<T>());

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

How can I extend this to the check I want to perform? Or how can I do it in a different way?

Community
  • 1
  • 1
Aart Stuurman
  • 3,188
  • 4
  • 26
  • 44

3 Answers3

13

I would use:

if constexpr(std::is_constructible<T, Archive&>{})

This trait lives in <type_traits> and comes ready-made and debugged for you. There is an entire family of traits to detect variants of this question available for you there.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
1

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.

Community
  • 1
  • 1
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 1
    Your answer teached me how to do this myself. Howards answer showed me how I can skip a lot of code(I only have a homebrew is_detected available). Both were very useful. – Aart Stuurman Apr 24 '17 at 16:19
  • Is there a way to prevent implicit conversions from screwing up the detection? I.e. if I have want to check for constructor by pointer, but the other class has a bool constructor, it will still succeed: https://wandbox.org/permlink/Zqm29gL1XLBgwlO2 – Jack White Aug 02 '17 at 11:06
0
template <class T>
using HasUnarchiveConstructor = std::is_constructible<T, Archive&>;

See wandbox

Carlo Wood
  • 5,648
  • 2
  • 35
  • 47