Reputation: 1503
Consider the function:
template<typename T>
void printme(T&& t) {
for (auto i : t)
std::cout << i;
}
or any other function that expects one parameter with a begin()/end() - enabled type.
Why is the following illegal?
printme({'a', 'b', 'c'});
When all these are legitimate:
printme(std::vector<char>({'a', 'b', 'c'}));
printme(std::string("abc"));
printme(std::array<char, 3> {'a', 'b', 'c'});
We can even write this:
const auto il = {'a', 'b', 'c'};
printme(il);
or
printme<std::initializer_list<char>>({'a', 'b', 'c'});
Upvotes: 50
Views: 15730
Reputation: 11
i found a solution that does not work for the provided printme
function in the question, but for similar ones:
this is what i started with:
template <typename V, typename C>
bool in(const C& container, const V& element)
{
return std::find(container.begin(), container.end(), element) != container.end();
}
which fails to compile for eg:
in({1, 2, 3}, 1);
with
Candidate template ignored: couldn't infer template argument 'C'
based off another answer here, we could add a template specialization for std::initializer_list
:
template <typename V, typename C>
bool in(const C& container, const V& element)
{
return std::find(container.begin(), container.end(), element) != container.end();
}
template <typename V>
bool in(const std::initializer_list<V>& container, const V& element)
{
return std::find(container.begin(), container.end(), element) != container.end();
}
which looks silly.
but the following also works:
template <typename V, typename C = std::initializer_list<V>>
bool in(const C& container, const V& element)
{
return std::find(container.begin(), container.end(), element) != container.end();
}
Upvotes: 0
Reputation: 439
You can also overload the function to explicitly take an argument of type initializer_list.
template<typename T>
void printme(std::initializer_list<T> t) {
for (auto i : t)
std::cout << i;
}
Upvotes: 14
Reputation: 185671
Your first line printme({'a', 'b', 'c'})
is illegal because the template argument T
could not be inferred. If you explicitly specify the template argument it will work, e.g. printme<vector<char>>({'a', 'b', 'c'})
or printme<initializer_list<char>>({'a', 'b', 'c'})
.
The other ones you listed are legal because the argument has a well-defined type, so the template argument T
can be deduced just fine.
Your snippet with auto
also works because il
is considered to be of type std::initializer_list<char>
, and therefore the template argument to printme()
can be deduced.
The only "funny" part here is that auto
will pick the type std::initializer_list<char>
but the template argument will not. This is because § 14.8.2.5/5 of the C++11 standard explicitly states that this is a non-deduced context for a template argument:
A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter does not have std::initializer_list or reference to possibly cv-qualified std::initializer_list type. [Example:
template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T
— end example ]
However with auto
, § 7.1.6.4/6 has explicit support for std::initializer_list<>
if the initializer is a braced-init-list (8.5.4), with
std::initializer_list<U>
.
Upvotes: 54
Reputation: 109119
This is specifically covered under § 14.8.2.5/5
A function parameter for which the associated argument is an initializer list but the parameter does not have
std::initializer_list
or reference to possibly cv-qualifiedstd::initializer_list
type. [ Example:template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T
—end example ]
To make it work, you can specify the template argument type explicitly.
printme<std::initializer_list<int>>( {1,2,3,4} );
Upvotes: 7