簡體   English   中英

std :: tuple與泛型類型,如boost :: any

[英]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構造函數!

一旦發生這種情況,就直接復制構建而言,你有點不幸。 你必須做一些事情,比如禁用tupleanything構造函數? 那似乎......不太好。 但在這種情況下,標記該構造函數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.

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