Reputation: 668
#include <vector>
using std::size_t;
struct Foo
{
Foo(size_t i, char c) {}
};
Foo Bar1()
{
size_t i = 0;
char c = 'x';
return { i, c }; // good
}
std::vector<char> Bar2()
{
size_t i = 0;
char c = 'x';
return { i, c }; // bad
}
https://wandbox.org/permlink/87uD1ikpMkThPTaw
warning: narrowing conversion of 'i' from 'std::size_t {aka long unsigned int}' to 'char' inside { }
Obviously it tries to use the initializer_list of vector. But why doesn't it use the better match vector<char>(size_t, char)
?
Can i use the desired constructor in a return statement without writing the type again?
Upvotes: 2
Views: 397
Reputation: 71899
Because initializer_list constructors, if at all possible, take precedence over other constructors. This is to make edge cases less confusing - specifically, this particular vector constructor that you expect it to use was deemed too easily selected by accident.
Specifically, the standard says in 16.3.1.7 "Initialization by list-initialization" [over.match.list] (latest draft, N4687):
(1) When objects of non-aggregate class type T are list-initialized such that 11.6.4 specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor in two phases:
- Initially, the candidate functions are the initializer-list constructors (11.6.4) of the class T and the argument list consists of the initializer list as a single argument.
- If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.
So if you do std::vector<char>( i, c )
, this section does not apply at all, since it isn't list-initialization. Normal overload resolution is applied, the (size_t, char)
constructor is found, and used.
But if you do std::vector<char>{ i, c }
, this is list-initialization. The initializer list constructors are tried first, and the (initializer_list<char>)
constructor is a match (even though it involves the narrowing conversion from size_t
to char
), so it is used before the size+value constructor is ever considered.
So to answer the edited-in question: no, you can't create the vector without naming its type. But in C++17 you can use class template argument deduction and simply write return std::vector(i, c);
Upvotes: 5