提问人:Zebrafish 提问时间:6/5/2022 最后编辑:user12002570Zebrafish 更新时间:8/26/2022 访问量:1664
为什么我不能用 l 值初始化这个 std::vector?
Why can't I initialize this std::vector with an l-value?
问:
我遇到了一个有趣的问题,我不明白发生了什么:
/* I WANT 6 ELEMENTS */
int lvalue = 6;
std::vector<int*> myvector { 6 }; /* WORKS FINE */
std::vector<int*> myvector{ lvalue }; /* DOESN'T WORK */
/* Element '1': conversion from 'int' to 'const unsigned __int64 requires a narrowing conversion */
据我所知,我提供的单个整数参数可以解释为调用带有参数的构造函数,也可以解释为采用初始值设定项列表的构造函数。它似乎只在我提供 l 值时才调用构造函数,但当我提供 r 值时才调用构造函数(好吧,至少是文字)。为什么会这样?size_type count
initialiser_list
size_t count
int
这也意味着:
int num_elements = 6;
std::vector<int> myvector{num_elements};
生成仅大小的向量1
;
std::vector<int> myvector(num_elements);
导致 大小 的向量,但我认为应该避免这种初始化,因为偶尔会遇到最棘手的解析问题。num_elements
答:
TL的;博士
该问题不是特定/限于的,而是下面从标准中引用的规则的结果。std::vector
让我们根据具体情况看看发生了什么,以及为什么我们在使用时会得到提到的缩小转换错误/警告。lvalue
案例 1
在这里,我们考虑:
int lvalue = 6; // lvalue is not a constant expression
//---------------------------v------------------->constant expression so works fine
std::vector<int*> myvector { 6 };
std::vector<int*> myvector{ lvalue };
//--------------------------^^^^^^--------------->not a constant expression so doesn't work
首先注意的是,没有采用初始值设定项列表的初始值设定项列表的初始值设定项列表。std::vector<int*>
int
因此,在这种情况下,将使用 ctor。现在让我们看看缩小转换错误/警告的原因。size_t count
当使用名为 while not 的变量时,我们收到错误/警告的原因是,在前一种情况下,它不是常量表达式,因此我们有一个缩小的转换。这可以从 dcl.init.list#7 中看出,它指出:lvalue
int
lvalue
缩小转换范围是隐式转换
- 从整数类型或无作用域枚举类型到不能表示原始类型的所有值的整数类型,除非源是常量表达式,其整数升级后的值将适合目标类型。
(强调我的)
这意味着从向量 ctor 的类型为参数的转换(左值表达式)是缩小转换。但是从 prvalue int 到向量参数的转换并不是缩小转换。lvalue
int
size_t
std::vector::vector(size_t, /*other parameters*/)
6
size_t
std::vector::vector(size_t, /*other parameters*/)
为了证明情况确实如此,让我们看一些例子:
示例 1
int main()
{
//----------------v---->no warning as constant expression
std::size_t a{1};
int i = 1;
//----------------v---->warning here i is not a constant expression
std::size_t b{i};
constexpr int j = 1;
//----------------v---->no warning here as j is a constexpr expression
std::size_t c{j};
return 0;
}
示例 2
struct Custom
{
Custom(std::size_t)
{
}
};
int main()
{
//-----------v---->constant expression
Custom c{3}; //no warning/error here as there is no narrowing conversion
int i = 3; //not a constant expressoion
//-----------v---->not a constant expression and so we get warning/error
Custom d{i}; //warning here of narrowing conversion here
constexpr int j = 3; //constant expression
//-----------v------>no warning here as j is a constant expression and so there is no narrowing conversion
Custom e{j};
return 0;
}
案例 2
在这里,我们考虑:
//------------v-------------------------->note the int here instead of int* unlike case 1
std::vector<int> myvector{num_elements};//this uses constructor initializer list ctor
在这种情况下,有一个初始值设定项列表 ctor 可用于,它将优先于构造函数,因为我们在这里使用了大括号而不是括号。因此,将创建一个大小的向量。有关更多详细信息,请参阅为什么在使用带支撑的初始值设定项列表时首选 std::initializer_list 构造函数?。std::vector<int>
size_t count
{}
()
1
另一方面,当我们使用:
std::vector<int> myvector(num_elements); //this uses size_t ctor
这里 的 ctor 将用作初始值设定项列表 ctor 在这种情况下甚至不可行,因为我们使用了括号。因此,将创建一个大小的向量。您可以使用下面给出的示例来确认这一点:size_t
std::vector
()
6
struct Custom
{
Custom(std::size_t)
{
std::cout<<"size t"<<std::endl;
}
Custom(std::initializer_list<int>)
{
std::cout<<"initializer_list ctor"<<std::endl;
}
};
int main()
{
Custom c(3); //uses size_t ctor, as the initializer_list ctor is not viable
return 0;
}
评论
std::vector<int> myvector{num_elements};
std::vector<int>
std::vector<int*>
std::vector<int*>