TL;DR
问题并不具体/仅限于std::vector,而是标准下面的quoted 规则的结果。
让我们逐个查看发生了什么,以及为什么在使用lvalue 时会收到上述缩小转换错误/警告。
案例一
这里我们考虑:
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 的初始化列表的初始化列表构造函数。
所以在这种情况下,size_t count ctor 将被使用。现在让我们看看缩小转换错误/警告的原因。
我们在使用名为lvalue 的变量时得到错误/警告而在使用prvalue int 时没有得到错误/警告的原因是因为在前一种情况下lvalue 不是一个常量表达式 并且所以我们有一个缩小的转换。这可以从dcl.init.list#7 中看出:
窄化转换是隐式转换
- 从整数类型或无作用域枚举类型到不能表示原始类型的所有值的整数类型,除非源是常量表达式,其值在整数提升后将适合目标类型。
(强调我的)
这意味着从int 类型的lvalue(它是一个左值表达式)到向量的std::vector::vector(size_t, /*other parameters*/) ctor 的size_t 参数的转换是一个窄化转换 .但是从纯右值 int 6 到向量的 std::vector::vector(size_t, /*other parameters*/) 的 size_t 参数的转换不是窄化转换。
为了证明确实如此,让我们看一些例子:
示例 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;
}
Demo
案例 2
这里我们考虑:
//------------v-------------------------->note the int here instead of int* unlike case 1
std::vector<int> myvector{num_elements};//this uses constructor initializer list ctor
在这种情况下,有一个可用于 std::vector<int> 的初始化列表 ctor,它会首选而不是 size_t count 构造函数,因为我们在这里使用了大括号 {} 而不是括号 @987654347 @。因此将创建一个大小为1 的向量。更多详情请访问Why is the std::initializer_list constructor preferred when using a braced initializer list?。
另一方面,当我们使用:
std::vector<int> myvector(num_elements); //this uses size_t ctor
这里 std::vector 的 size_t ctor 将用作初始化列表 ctor 在这种情况下甚至不可行,因为我们使用了括号 ()。因此将创建一个大小为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;
}