2017-05-24 72 views
6

考慮下面的代碼,使用g ++編譯7.0.1(-std = C++ 17):的std ::元組映射到元組,並使用佈設

#include <map> 
#include <tuple> 

int main() 
{ 
    // Create an alias for a tuple of three ints 
    using ThreeTuple=std::tuple<int,int,int>; 
    // Create an alias for a map of tuple to tuple (of three ints) 
    using MapThreeTupleToThreeTuple=std::map<ThreeTuple,ThreeTuple>; 

    MapThreeTupleToThreeTuple m; 

    // The following does NOT compile 
    m.emplace({1,2,3},{4,5,6}); 

    // ..., and neither does this 
    m.emplace(std::piecewise_construct,{1,2,3},{4,5,6}); 
} 

我曾想到的是,initializer_list參數到map::emplace()就足夠了,並且會導致按照指定將元組鍵值插入到元組值關聯中。顯然,編譯器不同意。

當然,明確地創建一個元組(即ThreeTuple{1,2,3},而不是僅僅{1,2,3}),並傳遞一個以map::emplace()解決了這個問題,但爲什麼不能在初始化列表被直接傳遞給map::emplace()這會自動將它們轉發到元組構造?

+2

初始值設定項列表沒有類型。 'emplace'依靠類型扣除。你無法推斷出無類型的東西。 – NathanOliver

+0

關於這個問題,你認爲C++ 17中有什麼改變? – Walter

+0

@Walter,我並不是想暗示我正在使用新的C++ 17功能,只是說我正在使用C++ 17兼容級別。 –

回答

5

但爲什麼不能在初始化列表直接因爲初始化列表是沒有表情,所以他們不必類型傳遞給map::emplace()

。爲emplace()簽名就是:

template< class... Args > 
std::pair<iterator,bool> emplace(Args&&... args); 

,你不能從{1,2,3}推斷一個類型。你無法在C++ 11中使用C++ 1z。此規則的唯一例外是模板參數的形式爲std::initializer_list<T>,其中T是模板參數。

爲了m.emplace({1,2,3},{4,5,6});工作,你需要一個像簽名:

std::pair<iterator,bool> emplace(key_type&&, mapped_type&&); 
+0

帶有'std :: piecewise_construct'的版本怎麼樣?同樣的問題? –

+0

@MichaelGoldshteyn是的。它仍然只是一個'emplace()'函數模板。 – Barry

+2

@MichaelGoldshteyn請注意,有'initializer_list'並且有* initializer list *。一個'initializer_list'可以從*初始化列表*構造,因爲它知道類型應該是什麼。普通的模板類型不會失敗。 – NathanOliver

5

據我所知,在C++在這方面17件事沒有改變。正如NathanOliver和Barry所解釋的,{1,2,3}不能被推斷爲具有任何類型,因此無法與模板參數進行匹配。您必須提供的參數爲ThreeTuple作爲抵扣類型的構造,即

m.emplace(std::piecewise_construct, 
      std::forward_as_tuple(1,2,3), 
      std::forward_as_tuple(4,5,6)); 

它調用構造函數

template<typename T1, typename T2> 
template<typename... Args1, typename... Args2 > 
std::pair<T1,T2>::pair(std::piecewise_construct_t, 
         std::tuple<Args1...>, std::tuple<Args2...>); 

在這種特殊情況下,你甚至可以省略std::piecewise_construct

m.emplace(std::forward_as_tuple(1,2,3), 
      std::forward_as_tuple(4,5,6)); 

或(在Nicol的評論中指出的C++ 17中)

m.emplace(std::tuple(1,2,3), std::tuple(4,5,6)); 

,對等同

m.emplace(ThreeTuple(1,2,3), ThreeTuple(4,5,6)); 

和調用構造函數

template<typename T1, typename T2> 
std::pair<T1,T2>::pair(const&T1, const&T2); 

還請注意,AFAIK你不能使用std::initializer_list<int>明確地得到這個工作。原因很簡單,pair<ThreeTuple,ThreeTuple>(地圖的value_type)沒有合適的構造函數。

+0

有一個+1顯示一個工作的例子如何正確地做到這一點。 –

+0

僅供參考:由於OP使用C++ 17,因此您也可以使用模板類推導,因此只需使用'std :: tuple(1,2,3)'。假設編譯器支持它並且標準庫正確地實現它。 –

+0

@NicolBolas正確,但這與OP提到的'ThreeTuple(1,2,3)'幾乎相同。無論如何,我改變了答案。 – Walter