[英]std::tuple with generic types like boost::any
親愛的程序員,
下面的代碼讓我有些頭疼。 它嘗試將一個“泛型”對象(=可以從任何東西構造的對象)添加到元組,然后復制該元組。
#include <tuple>
#include <iostream>
#include <typeinfo>
struct anything
{
anything()
{}
anything(const anything&)
{
std::cout << "Called copy c'tor" << std::endl;
}
template<class T>
anything(T arg)
{
std::cout << "Called c'tor with with argument of type " << typeid(arg).name() << std::endl;
// new T(arg); // causes stack overflow
}
};
int main()
{
std::tuple<anything> t;
//
std::cout << "Copy constructing t2, expecting copy c'tor to be called." << std::endl;
std::tuple<anything> t2(t);
return 0;
}
使用VS 2015 Update 2,它甚至無法編譯
std::tuple<anything> t2(t);
在tuple.h中深入觸發編譯器錯誤。 使用gcc 5.3.1編譯,但輸出不是我所期望的:
復制構造t2,期望復制c'tor被調用。
叫拷貝c'tor
用類型為St5tupleIJ8anythingEE的參數調用c'tor
我不明白的是最后一行,即為什么模板化的構造函數被調用std :: tuple作為參數?
這實際上是一個現實世界的問題。 在我的應用程序中,我使用了一個boost :: signal簽名
typedef boost::type_erasure::any
<boost::mpl::vector<
boost::type_erasure::typeid_<>,
boost::type_erasure::copy_constructible<>,
boost::type_erasure::less_than_comparable<>,
boost::type_erasure::equality_comparable<>,
boost::type_erasure::relaxed
>> KeyType;
boost::signals2::signal<void(const KeyType&)>
在使用它調用slot函數之前,Boost信號在內部將參數包裝在元組中,最終導致堆棧溢出,因為元組c'tor以元組作為參數調用任何c'tor,然后任何c'tor調用tuple c'tor與'任何一個元組'等等等等......
讓我們通過重載決議:
std::tuple<anything> t2(t);
我們有三個可行的構造函數可供我們使用:
explicit tuple( const Types&... args ); // (2) with Types = [anything]
template< class... UTypes >
explicit tuple( UTypes&&... args ); // (3) with UTypes = [std::tuple<anything>&]
tuple( const tuple& other ) = default; // (8)
哪些有這些參數列表:
tuple(const anything& ); // (2)
tuple(std::tuple<anything>& ); // (3)
tuple(std::tuple<anything> const& ); // (8)
(2)
涉及用戶定義的轉換,而(3)
和(8)
是完全匹配。 說到引用綁定:
標准轉換序列S1是比標准轉換序列S2更好的轉換序列,如果S1和S2是引用綁定(8.5.3),並且引用引用的類型是相同類型,除了頂級cv -qualifiers,以及由S2引用的引用引用的類型比由S1引用的引用引用的類型更符合cv。
所以(3)
是首選 - 因為它比(8)
更少cv資格。 該構造函數調用anything(std::tuple<anything> )
。
至於解決方案,你需要的是(3)
在這種情況下不被考慮 - 我們需要使它不是一個可行的選擇(因為(8)
已經優於(2)
)。 目前 ,最簡單的方法是使您的構造函數explicit
:
template<class T>
explicit anything(T arg) { ... }
這是有效的,因為(3)
是根據is_convertible<>
指定的,它將在explicit
轉換時返回false。 然而,這當前被認為是一個缺陷,將來可能會發生變化 - 畢竟,我們在這里明確地構建了每個方面,所以仍然應該考慮explicit
構造函數!
一旦發生這種情況,就直接復制構建而言,你有點不幸。 你必須做一些事情,比如禁用tuple
的anything
構造函數? 那似乎......不太好。 但在這種情況下,標記該構造函數explicit
仍然適用於復制初始化:
std::tuple<anything> t2 = t;
由於上述相同的缺陷,即使沒有標記anything
構造explicit
,它現在也可以工作。
如果你看一下元組的實現,你會注意到這一點
_Tuple_val<_This> _Myfirst; // the stored element
...
template<class _This2,
class... _Rest2,
class = typename _Tuple_enable<tuple<_This2, _Rest2...>, _Myt>::type>
explicit tuple(_This2&& _This_arg, _Rest2&&... _Rest_arg)
: _Mybase(_STD forward<_Rest2>(_Rest_arg)...),
_Myfirst(_STD forward<_This2>(_This_arg))
{ // construct from one or more moved elements
}
元組的構造函數將第一個參數傳遞給元組的第一個元素的構造函數。 由於變量t
具有類型std::tuple<anything>
編譯器找到與t
構造anything
的最佳匹配 - 模板構造函數。
要復制元組,您只需要編寫
std::tuple<anything> t2 = t;
UPD。
根據標准std :: tuple提供以下構造函數:
template <class... UTypes>
explicit constexpr tuple(const Types&...);
template <class... UTypes>
constexpr tuple(const tuple<UTypes...>&);
顯然你的模板構造函數比元組的復制構造函數更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.