c++ - 为什么我的编译器无法计算出这种转换,它何时存在?
问题描述
看起来当我创建std::initializer_list<B*>
whereclass B
派生class A
并将其传递给接受 a 的函数std::initializer_list<A*>
时,编译器会感到困惑。但是,如果我std::initializer_list<B*>
用大括号初始化器就地创建了一个(我假设它是一个临时std::initializer_list
的),它就可以很好地转换它。
具体来说,它似乎无法转换std::initializer_list<B*>
为std::initializer_list<A*>
,即使某些转换明显存在,正如此处的一个工作函数调用所证明的那样。
这种行为的解释是什么?
#include <initializer_list>
#include <iostream>
class A {
public:
A() {}
};
class B : public A {
public:
B() {}
};
using namespace std;
void do_nothing(std::initializer_list<A*> l) {
}
int main() {
B* b1;
B* b2;
std::initializer_list<B*> blist = {b1, b2};
//error, note: candidate function not viable: no known conversion from
//'initializer_list<B *>' to 'initializer_list<A *>' for 1st argument
//do_nothing(blist);
//Totally fine, handles conversion.
do_nothing({b1, b2});
return 0;
}
编辑:
作为一种解决方法,这样做
std::initializer_list<A*> alist = {b1, b2};
似乎被接受,do_nothing()
但我仍然对这种行为感到好奇。
解决方案
原因是这里的初始化列表
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*>
注意术语braced-init-list和initializer_list。虽然看起来很相似,但这是两个非常不同的东西。
一个花括号初始化列表是一对花括号,其间有值,如下所示:
{ 1, 2, 3, 4 }
或这个:
{ 1, 3.14, "different types" }
它是一种用于初始化的特殊结构,在 C++ 语言中有自己的规则。
另一方面,std::initializer_list
is 只是一个类型(实际上是一个模板,但我们在这里忽略了这个事实,因为它并不重要)。从该类型中,您可以创建一个对象(就像您对您的 所做的那样blist
)并初始化该对象。因为braced-init-list是一种初始化形式,我们可以在std::initializer_list
:
std::initializer_list<int> my_list = { 1, 2, 3, 4 };
因为 C++ 有一个特殊规则,允许我们用花括号初始化列表初始化每个函数参数,所以可以do_nothing({b1, b2});
编译。这也适用于多个参数:
void do_something(std::vector<int> vec, std::tuple<int, std::string, std::string> tup)
{
// ...
}
do_something({1, 2, 3, 4}, {10, "first", "and 2nd string"});
或嵌套初始化:
void do_something(std::tuple<std::tuple<int, std::string>, std::tuple<int, int, int>, double> tup)
{
// ...
}
do_something({{1, "text"}, {2, 3, 4}, 3.14});
推荐阅读
- python - 编写循环:Beautifulsoup 和 lxml 用于在逐页跳过设置中获取页面内容
- c++ - 将指针传递给 std::thread 时的数据竞争
- r - R 组成 n 个子群的群(超群)
- google-api - 来自 html 的谷歌文档 - 不一致地包括图像
- c++ - 使用 qt-faststart 和“ffmpeg -movflags +faststart”的不同结果
- wpf - 对 WPF 和 Winform 中“Screen.Bounds”的返回感到困惑
- embedded-linux - Beaglebone dcan0 和 dcan1
- reactjs - 反应状态不同步
- excel - 从共享邮箱 Outlook 子文件夹中提取不工作
- json - Delphi TidHTTP POST:如何处理响应 JSON