initializer_list<T> can't convert to initializer_list<U>, but T convertable to U

最后都变了- 提交于 2021-01-28 04:16:28

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!