这样做的原因是这里的初始化列表
do_nothing({b1,b2});
与
的类型不同
std::initializer_list<B*> blist = {b1,b2};
由于do_nothing
在函数调用(std::initializer_list<A*>
)中使用了do_nothing({b1,b2})
括号初始化列表,因此用于根据函数参数构造std::initializer_list<A*>
。之所以有效,是因为B*
可隐式转换为A*
。但是,std::initializer_list<B*>
不能隐式转换为std::initializer_list<A*>
,因此会出现该编译器错误。
让我们写一些伪代码来演示会发生什么。首先,我们来看一下代码的工作部分:
do_nothing({b1,b2}); // call the function with a braced-init-list
// pseudo code starts here
do_nothing({b1,b2}): // we enter the function,here comes our braced-init-list
std::initializer_list<A*> l {b1,b2}; // this is our function parameter that gets initialized with whatever is in that braced-init-list
... // run the actual function body
现在不起作用了:
std::initializer_list<B*> blist = {b1,b2}; // creates an actual initializer_list
do_nothing(blist); // call the function with the initializer_list,NOT a braced-init-list
// pseudo code starts here
do_nothing(blist): // we enter the function,here comes our initializer_list
std::initializer_list<A*> l = blist; // now we try to convert an initializer_list<B*> to an initializer_list<A*> which simply isn't possible
... // we get a compiler error saying we can't convert between initializer_list<B*> and initializer_list<A*>
请注意术语 braised-init-list 和 initializer_list 。虽然看起来很相似,但这是两个截然不同的事情。
braced-init-list 是一对花括号,中间有两个值,如下所示:
{ 1,2,3,4 }
或者这个:
{ 1,3.14,"different types" }
它是用于初始化的特殊结构,它具有C ++语言自己的规则。
另一方面,std::initializer_list
只是一种类型(实际上是一个模板,但是我们在这里忽略了这一事实,因为它并不重要)。通过该类型,您可以创建一个对象(就像您使用blist
一样)并初始化该对象。而且因为 braced-init-list 是一种初始化形式,所以我们可以在std::initializer_list
上使用它:
std::initializer_list<int> my_list = { 1,4 };
因为C ++有一条特殊的规则,该规则允许我们使用 braced-init-list 初始化每个函数参数,因此do_nothing({b1,b2});
进行编译。这也适用于多个参数:
void do_something(std::vector<int> vec,std::tuple<int,std::string,std::string> tup)
{
// ...
}
do_something({1,4},{10,"first","and 2nd string"});
或嵌套初始化:
void do_something(std::tuple<std::tuple<int,std::string>,int,int>,double> tup)
{
// ...
}
do_something({{1,"text"},{2,3.14});
,
我们必须区分std::initializer_list<T>
对象和 braced-init-list 。
Braced-init-list 用大括号括起来,用逗号分隔,并且是源代码级的构造。可以在各种上下文中找到它们,并且它们的元素不必都属于同一类型。例如
std::pair<int,std::string> = {47,"foo"}; // <- braced-init-list
当执行包含 braised-init-list 的语句时,有时会根据std::initializer_list<T>对象(对于某些T
) >括号初始列表。这些上下文是:
- 当声明为
std::initializer_list<T>
类型的变量具有 braced-init-list 作为其初始化程序时;和
- 当对象以
auto
类型声明并从非空的 braced-init-list 复制初始化时。
如果无法从 braced-init-list 创建std::initializer_list<T>
对象,则会发生编译错误。例如
std::initializer_list<int> l = {47,"foo"}; // error
除了上述创建std::initializer_list
对象的方法外,还有另一种方法:通过复制现有对象。这是一个浅表副本(它只是使新对象指向与旧对象相同的数组)。当然,副本不会更改类型。
现在返回您的代码。当您尝试使用参数do_nothing
调用blist
函数时,您尝试做的事情是不可能的,因为您提供的内容不能用于创建std::initializer_list<A*>
对象。创建此类对象的唯一方法是从 braised-init-list 或现有的std::initializer_list<A*>
对象。
但是,将{b2,b1}
作为参数可以正常工作,因为它是 braced-init-list 。允许从 braced-init-list 的元素到所需元素类型的隐式转换。
,
std::initializer_list
是模板。
通常在C ++中,即使some_template<T>
与some_template<U>
相关,T
与U
也无关。
在调用do_something({b1,b2})
中,编译器实际上创建了std::initializer_list<A*>
(在应用list-initialization规则之后)。
您可以将do_nothing
模板化为其他类型:
template<typename T,typename = std::enable_if_t<std::is_convertible<T,A>::value>>
void do_nothing(std::initializer_list<T*> l) {
}
(可选的SFINAE部分将T
限制为可转换为A
的类型)
有关更多可能的解决方案,请参考this related question。
本文链接:https://www.f2er.com/2755897.html