簡體   English   中英

為什么我不能用左值初始化這個 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 */

從我可以看到,我提供的單個整數參數可以解釋為使用參數size_type count調用構造函數,也可以解釋為使用初始化列表的構造函數。 似乎只有在我提供 l 值時才調用initialiser_list構造函數,但在我提供 r 值int時才調用size_t count構造函數(好吧,至少是文字)。 為什么是這樣?

這也意味着:

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的初始化列表。

所以在這種情況下,將使用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;
}

演示


案例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構造函數更可取,因為我們在這里使用了大括號{}而不是括號() 因此將創建一個大小為1的向量。 更多詳細信息,請參閱為什么在使用花括號初始值設定項列表時首選 std::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; 
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM