Why the Variadic Constructor with std::is_constructible Fails to Handle Initializer List Initialization?

44 views Asked by At

I have a C++11 class named Prop that looks like this:

#include <functional>
#include <vector>

template <typename T>
class Prop {
public:
    template <typename... Args, typename = typename std::enable_if<std::is_constructible<T, Args...>::value>::type>
    Prop(Args &&...args) {
        const auto val = T(std::forward<Args>(args)...);
        getter = [=]() { return val; };
    }

    T operator()() const {
        return getter();
    }

private:
    std::function<T()> getter;
};

When I try to create a Prop object with an initializer list like this:

int main() {
    auto prop = Prop<std::vector<int>>({ 1, 2, 3 });
    return 0;
}

I get a compilation error like this:

/Users/tempus/Desktop/liquid/src/main.cpp:32:17: error: no matching constructor for initialization of 'Prop<std::vector<int>>'
    auto prop = Prop<std::vector<int>>({ 1, 2, 3 });
                ^                      ~~~~~~~~~~~
/Users/tempus/Desktop/liquid/src/main.cpp:5:7: note: candidate constructor (the implicit copy constructor) not viable: cannot convert initializer list argument to 'const Prop<std::vector<int>>'
class Prop {
      ^
/Users/tempus/Desktop/liquid/src/main.cpp:5:7: note: candidate constructor (the implicit move constructor) not viable: cannot convert initializer list argument to 'Prop<std::vector<int>>'
class Prop {
      ^
/Users/tempus/Desktop/liquid/src/main.cpp:14:5: note: candidate template ignored: substitution failure: deduced incomplete pack <(no value)> for template parameter 'Args'
    Prop(Args &&...args) {
    ^
1 error generated.

However, if I add the following constructor to the Prop class:

template <typename U, typename = typename std::enable_if<std::is_constructible<T, std::initializer_list<U>>::value>::type>
Prop(const std::initializer_list<U> &list) {
    const auto val = T(list);
    getter = [=]() { return val; };
}

the error goes away, and the code compiles successfully.

In the case of auto prop = Prop<std::vector<int>>({ 1, 2, 3 });, the Args type should be deduced as std::initializer_list<int>, and std::vector<int> is constructible from std::initializer_list<int>. So, I would expect the Prop(Args &&...args) constructor to handle this case.

Why does the absence of the std::initializer_list constructor cause a compilation error when creating a Prop object with an initializer list, even though the Prop(Args &&...args) constructor seems to be a match for std::initializer_list<int>? What is the role of the std::initializer_list constructor in this case, and why is it necessary for the code to compile successfully?

0

There are 0 answers