Search code examples
c++template-meta-programmingc++17

Detect if class has constructor with signature


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?


Solution

  • 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.