问题
Considering the following code snippet...
void boo(std::initializer_list<unsigned> l)
{
}
template <class T>
void foo(std::initializer_list<T> l)
{
//Even though T is convertable, initializer list is not...:-(
boo(move(l));
}
int main()
{
foo({1u,2u,3u}); //Compiles fine as expected
foo({1,2,3}); //Fails to compile at line 9... - could not convert...
return 0;
}
... I'm surprised that initializer_list<int>
is not convertible to initializer_list<unsigned>
, event though int converts to unsigned.
I've been wondering in what way one can write foo to allow the conversion. Can one somehow unwrap the list having the wrong type and recreate a new list with the right type?
回答1:
While you can't unpack initializer list during compile time (to perform neccessary conversion), you can create it the way you want it to be. Consider following code:
#include <initializer_list>
void boo(std::initializer_list<unsigned> l);
template <class... T>
void foo(T... l)
{
boo({static_cast<unsigned int>(l)...});
}
int main()
{
foo(1,2,3);
return 0;
}
回答2:
Nope. Constructing an initializer list requires a compile-time known length, and consuming an initializer list you do not have a compile-time known length.
initializer lists are intended for "user-facing" operations, where the user is the consumer of your interface. Using them internally like that doesn't work well.
An approach you can use is to write an array_view<T> type that wraps a range of contiguous memory (an initializer list, a vector, a std array, or a C array are all examples of this) in a range-view like way (with begin, end, data, size, empty, front, back methods. Iterators are pointers).
Then give it implicit converting ctors from vector<T>&
, vector<std::remove_const_t<T>> const&
, initializer_list<std::remove_const_t<T>>
etc.
void boo(array_view<const unsigned> l)
{
}
template <class T>
void foo(std::initializer_list<T> l)
{
boo(std::vector<unsigned>{l.begin(), l.end()});
}
goes an reallocates a new buffer of unsigned
values based off whatever was in l
and passes it to boo
. boo
consumes an array_view<unsigned const>
, which can convert from a vector
or an initializer_list
of unsigned
.
We can then write maybe_convert_list
:
template <class T, class U, class...LowPrecidence>
std::vector<T> maybe_convert_list(std::initializer_list<U> l, LowPrecidence&&...)
{
return {l.begin(), l.end()};
}
template <class T>
std::initializer_list<T> maybe_convert_list(std::initializer_list<T> l)
{
return l;
}
template <class T>
void foo(std::initializer_list<T> l)
{
boo(maybe_convert_list<unsigned>(l));
}
or somesuch. It leaves initializer_list<unsigned>
alone. For other types of lists, it converts it to a std::vector<unsigned>
.
回答3:
A class Foo<T>
is not convertible to Foo<U>
, even though T
is convertible to U
. For the compiler, different types in the template instantiation give rise to instances of unrelated types.
So in your case, foo({1,2,3})
deduces T
as int
, so the argument of foo
has the type initializer_list<int>
. You then try to pass that to boo
, which takes an initializer_list<unsigned>
, which is unrelated to initializer_list<int>
, hence the compile error.
You can probably avoid this headache via template specialization instead, i.e. specialize your foo
for the unsigned
type:
template<>
void foo<unsigned>(std::initializer_list<unsigned>)
{
// specialization here
}
回答4:
In short, this conversion cannot be done. Once you have a std::initializer_list<int>
object, there is no way to use it to synthesize a std::initializer_list<unsigned>
. You can iterate through it but the problem is that the information about the size is not available statically so there's no way to generate from one std::initializer_list
object a braced-enclosed list of initializers with which to construct a different std::initializer_list
object.
If boo
needs to receive std::initializer_list<unsigned>
, then foo
should have a parameter of type std::initializer_list<unsigned>
. You can convert {1, 2, 3}
to std::initializer_list<unsigned>
. But once you deduce it as std::initializer_list<int>
this possibility disappears.
来源:https://stackoverflow.com/questions/34005917/initializer-listt-cant-convert-to-initializer-listu-but-t-convertable-to-u