【问题标题】:Why can't I initialize this std::vector with an l-value?为什么我不能用左值初始化这个 std::vector?
【发布时间】:2022-06-10 17:43:37
【问题描述】:

我遇到了一个有趣的问题,但我不明白发生了什么:

/* 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 */

据我所知,我提供的单个整数参数可以解释为使用参数size_type count 调用构造函数,或者使用初始化列表调用构造函数。似乎只有在我提供左值时才调用initialiser_list 构造函数,但在我给出右值int 时才调用size_t count 构造函数(嗯,至少是文字)。这是为什么呢?

这也意味着:

int num_elements = 6;
std::vector<int> myvector{num_elements};

产生一个只有1大小的向量;

std::vector<int> myvector(num_elements);

生成大小为num_elements 的向量,但我认为应该避免这种初始化,因为偶尔会遇到最棘手的解析问题。

【问题讨论】:

  • @AnoopRana 我刚刚检查了我的编译器 Visual Studio 2022,它生成了一个大小为 1 的向量。
  • @AnoopRana 您的演示与询问者的代码不匹配,它添加了一组额外的大括号。对语法如何影响重载决议的确切解释似乎是有道理的。
  • 啊。看起来我们也在结合几个不同的东西。声称std::vector&lt;int&gt; myvector{num_elements}; 创建了一个带有一个元素的向量。确实如此,因为std::vector&lt;int&gt;std::vector&lt;int*&gt; 不同。 (它有一个构造函数接受 int 的初始化列表,而 std::vector&lt;int*&gt; 没有。)
  • Constructor 10 解释了std::vector::vector 的区别,并参见上面的注释,该示例专门解决了这个问题。
  • 这是关于“新”支架初始化的常见抱怨(尤其是必须向初学者教授此内容的教师和培训师)。他们甚至改变了语言版本之间的行为(一个突破性的变化),因为它太令人困惑了。委员会内部对此进行了很多辩论。但这仍然令人困惑。这是 C++ 的默认设置错误的原因之一。

标签: c++ language-lawyer narrowing


【解决方案1】:

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&lt;int*&gt; 没有采用 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&lt;int&gt; 的初始化列表 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::vectorsize_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;
}

【讨论】:

  • 知道了。谢谢。我误解了我不好的陈述。
猜你喜欢
  • 1970-01-01
  • 2020-10-10
  • 2021-10-26
  • 1970-01-01
  • 2013-06-06
  • 2020-10-28
  • 1970-01-01
  • 2021-03-19
  • 2017-03-15
相关资源
最近更新 更多