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?