Search code examples
c++variadic-templatessfinae

Variadic templated class - 0 length package constructor collision


Given

template<class... Args>
struct foo {

    function<void(Args...)>     m_function;
    unique_ptr<tuple<Args...>>  m_args;

    foo(const std::function<void(Args...)>& func, Args... args) :
        m_function{ func }, m_args{ new tuple<Args...>(args...) } {

        cout << "ctor 1" << endl;
    }

    // <some template wizardry here>
    foo(const std::function<void(Args...)>& func) :
        m_function{ func } {

        cout << "ctor 2" << endl;
    }
};

I would like ctor2 to be instantiated only when sizeof...(Args) != 0 (or else I get a collision..).

This right here seems to work (no collisions)

template<Args...>
foo(const std::function<void(Args...)>& func) :
    m_function{ func } {

    cout << "ctor 2" << endl;
}

but I have no idea how/why or if it's reliable.

Also Id probably use something like

std::enable_if<sizeof...(Args) != 0, ???>

How can I resolve this using std::enable_if and what is going on in my 2nd code sample ?


Solution

  • As pointed by Johannes Schaub - litb in a comment, you can simply add a variadic list of unused template parameters, just to transform your second contructor in a template one and give precedence (avoiding the collision) to the first one that isn't a template constructor.

    So you can simply write

    template <typename ...>
    foo (std::function<void(Args...)> const & func)
        : m_function{ func }
     { std::cout << "ctor 2" << std::endl; }
    

    But to satisfy your request

    I would like ctor2 to be instantiated only when sizeof...(Args) != 0

    you can try with (less elegant but, maybe, more comprehensible)

    template <bool B = (sizeof...(Args) > 0u),
              std::enable_if_t<B, bool> = true>
    foo (std::function<void(Args...)> const & func)
        : m_function{ func }
     { std::cout << "ctor 2" << std::endl; }