簡體   English   中英

std :: initializer_list作為函數參數

[英]std::initializer_list as function argument

出於某種原因,我認為C ++ 0x允許將std::initializer_list用作可期望可以從中構造類型的函數的函數參數,例如std::vector 但顯然,它不起作用。 這僅僅是我的編譯器,還是永遠無法工作? 是因為潛在的過載解決問題嗎?

#include <string>
#include <vector>

void function(std::vector<std::string> vec)
{
}

int main()
{
    // ok
    std::vector<std::string> vec {"hello", "world", "test"};

    // error: could not convert '{"hello", "world", "test"}' to 'std::vector...'
    function( {"hello", "world", "test"} );
}

GCC有一個錯誤。 該標准使之有效。 看到:

請注意,這有兩個方面

  • 一般如何進行初始化?
  • 在重載解析期間如何使用初始化,它的成本是多少?

第一個問題在8.5節中回答。 13.3節中回答了第二個問題。 例如,引用綁定在8.5.313.3.3.1.4處理,而列表初始化在8.5.413.3.3.1.5處理。

8.5/14,16

初始化形式

 T x = a; 

以及參數傳遞 ,函數返回,引發異常(15.1),處理異常(15.3)和聚合成員初始化(8.5.1)都稱為復制初始化。


初始化程序的語義如下[...]:如果初始化程序是一個括號初始列表,則該對象將進行列表初始化(8.5.4)。

在考慮候選function ,編譯器將看到一個初始化列表(尚無類型-它只是一個語法構造!)作為參數,而std::vector<std::string>作為function的參數。 為了弄清楚轉換成本是多少,以及我們是否可以在超載的情況下轉換這些成本, 13.3.3.1/5

13.3.3.1.5/1

當參數是初始化器列表(8.5.4)時,它不是表達式,並且特殊規則適用於將其轉換為參數類型。

13.3.3.1.5/3

否則,如果參數是非聚合類X,並且按照13.3.1.7的重載分辨率,從參數初始值設定項列表中選擇X的最佳最佳構造函數來執行類型X對象的初始化,則隱式轉換序列是用戶-定義的轉換順序。 用戶定義的轉換允許將初始化列表元素轉換為構造函數參數類型,除非13.3.3.1中有說明。

非聚合類Xstd::vector<std::string> ,我將在下面找出單個最佳構造函數。 最后一條規則允許我們在以下情況下使用用戶定義的轉換:

struct A { A(std::string); A(A const&); };
void f(A);
int main() { f({"hello"}); }

即使需要用戶定義的轉換,我們也可以將字符串文字轉換為std::string 但是,它指出了另一段的限制。 13.3.3.1說什么?

13.3.3.1/4 ,該段負責禁止多個用戶定義的轉換。 我們將僅關注列表初始化:

但是,當考慮將用戶定義的轉換函數[(或構造函數)]的參數作為[...] 13.3.1.7的候選對象時,將初始化程序列表作為單個參數傳遞時,或者當初始化程序列表中只有一個元素時graco.com graco.com X的構造函數的第一個參數考慮轉換為某類X或引用(可能是經過cv限定)X,或者只允許標准轉換序列和省略號轉換序列。

請注意,這是一個重要的限制:如果不是這樣做的話,上面的代碼可以使用copy-constructor建立同樣良好的轉換序列,並且初始化將是不明確的。 (請注意該規則中“ A或B和C”的潛在混淆:它的意思是“(A或B)和C”-因此, 只有在嘗試通過參數為的X的構造函數進行轉換時,我們才受到限制。輸入X )。

我們委托給13.3.1.7來收集可用於執行此轉換的構造函數。 讓我們從8.5開始,從一般的角度來處理本段,這將我們委托給8.5.4

8.5.4/1

列表初始化可以在直接初始化或復制初始化上下文中進行; 在直接初始化上下文中的列表初始化稱為直接列表初始化,而在復制初始化上下文中的列表初始化稱為copy-list-initialization

8.5.4/2

如果構造函數的第一個參數的類型為std::initializer_list<E>或對某些類型E的引用可能是經過cv限定的std::initializer_list<E> 構造函數,則該構造函數為初始化列表創建器 ,並且沒有其他參數,否則所有其他參數都有默認參數(8.3.6)。

8.5.4/3

對象或類型T的引用的列表初始化定義如下:[...]否則,如果T是類類型,則考慮構造函數。 如果T具有一個initializer-list構造函數,則參數列表由作為單個參數的初始化器列表組成; 否則,參數列表由初始化列表的元素組成。 列舉了適用的構造函數(13.3.1.7),並通過重載分辨率(13.3)選擇了最佳的構造函數。

此時, T是類類型std::vector<std::string> 我們有一個參數(尚無類型!我們只是在具有語法初始化器列表的上下文中)。 13.3.1.7枚舉構造13.3.1.7

[...]如果T具有初始化器列表構造函數(8.5.4),則參數列表由作為單個參數的初始化器列表組成; 否則,參數列表由初始化列表的元素組成。 對於復制列表初始化,候選函數都是T的所有構造函數。但是,如果選擇了顯式構造函數,則初始化格式不正確。

我們只會將std::vector的初始化列表視為唯一的候選對象,因為我們已經知道其他對象不會勝過它,也不適合該參數。 它具有以下簽名:

vector(initializer_list<std::string>, const Allocator& = Allocator());

現在,在13.3.3.1.5中列舉了將初始化列表轉換為std::initializer_list<T> (以對參數/參數轉換的成本進行分類):

當參數是初始化器列表(8.5.4)時,它不是表達式,並且特殊規則適用於將其轉換為參數類型。 [...]如果參數類型為std::initializer_list<X>並且初始化列表的所有元素都可以隱式轉換為X,則隱式轉換序列是將列表的元素轉換為X所需的最差轉換。即使在調用initializer-list構造函數的上下文中, 此轉換也可以是用戶定義的轉換

現在,初始化列表將成功轉換,轉換順序是用戶定義的轉換(從char const[N]std::string )。 8.5.4再次詳細說明了如何進行:

否則,如果T是std::initializer_list<E> ,則按如下所述構造一個initializer_list對象,並根據來自同一類型的類(8.5)的對象初始化規則將其用於初始化該對象。 (...)

參見8.5.4/4如何完成最后一步:)

似乎是這樣工作的:

function( {std::string("hello"), std::string("world"), std::string("test")} );

也許這是一個編譯器錯誤,但是也許您要進行太多的隱式轉換。

暫時,我不確定,但是我懷疑這里發生的是將其轉換為initializer_list是一次轉換,而將其轉換為vector是另一次轉換。 如果是這樣,您就超出了一次隱式轉換的限制...

這是編譯器錯誤,或者您的編譯器不支持std :: initializer_list。 在GCC 4.5.1上測試,編譯良好。

您必須指定您的initializer_list類型

function(std::initializer_list<std::string>{"hello", "world", "test"} );

祝好運

暫無
暫無

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

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