简体   繁体   English

std :: tuple与泛型类型,如boost :: any

[英]std::tuple with generic types like boost::any

Dear fellow programmers, 亲爱的程序员,

the code below gives me some headaches. 下面的代码让我有些头疼。 It tries to add a 'generic' object (=object that can be constructed from anything) to a tuple and then copy that tuple. 它尝试将一个“泛型”对象(=可以从任何东西构造的对象)添加到元组,然后复制该元组。

    #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;
    }

With VS 2015 Update 2 it doesn't even compile, the line 使用VS 2015 Update 2,它甚至无法编译

std::tuple<anything> t2(t);

triggers a compiler error deep in tuple.h. 在tuple.h中深入触发编译器错误。 With gcc 5.3.1 it compiles, but the output is not what I'd expect: 使用gcc 5.3.1编译,但输出不是我所期望的:

Copy constructing t2, expecting copy c'tor to be called. 复制构造t2,期望复制c'tor被调用。
Called copy c'tor 叫拷贝c'tor
Called c'tor with with argument of type St5tupleIJ8anythingEE 用类型为St5tupleIJ8anythingEE的参数调用c'tor

What I don't understand is the last line, ie why the templated constructor gets called with std::tuple as argument? 我不明白的是最后一行,即为什么模板化的构造函数被调用std :: tuple作为参数?

This is actually a real world problem. 这实际上是一个现实世界的问题。 In my application I use a boost::signal of signature 在我的应用程序中,我使用了一个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&)>

Boost signals internally wraps the argument in a tuple before calling the slot function with it, which eventually results in a stack overflow, because the tuple c'tor calls the any c'tor with tuple as argument and the any c'tor then calls the tuple c'tor with 'any of tuple' and so on and on and on... 在使用它调用slot函数之前,Boost信号在内部将参数包装在元组中,最终导致堆栈溢出,因为元组c'tor以元组作为参数调用任何c'tor,然后任何c'tor调用tuple c'tor与'任何一个元组'等等等等......

Let's go through overload resolution for: 让我们通过重载决议:

std::tuple<anything> t2(t);

We have three viable constructors at our disposal: 我们有三个可行的构造函数可供我们使用:

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)

Which have these argument lists: 哪些有这些参数列表:

tuple(const anything& );             // (2)
tuple(std::tuple<anything>& );       // (3)
tuple(std::tuple<anything> const& ); // (8)

(2) involves a user-defined conversion whereas (3) and (8) are exact matches. (2)涉及用户定义的转换,而(3)(8)是完全匹配。 When it comes to reference bindings: 说到引用绑定:

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [...] S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers. 标准转换序列S1是比标准转换序列S2更好的转换序列,如果S1和S2是引用绑定(8.5.3),并且引用引用的类型是相同类型,除了顶级cv -qualifiers,以及由S2引用的引用引用的类型比由S1引用的引用引用的类型更符合cv。

So (3) is preferred - since it's less cv-qualified than (8) . 所以(3)是首选 - 因为它比(8)更少cv资格。 That constructor calls anything(std::tuple<anything> ) . 该构造函数调用anything(std::tuple<anything> )


As far as solutions, what you need is for (3) to not be considered in this case - we need to make it not a viable option (since (8) is already preferred to (2) ). 至于解决方案,你需要的是(3)在这种情况下不被考虑 - 我们需要使它不是一个可行的选择(因为(8)已经优于(2) )。 Currently , the easiest thing is just to make your constructor explicit : 目前 ,最简单的方法是使您的构造函数explicit

template<class T>
explicit anything(T arg) { ... }

this works since (3) is specified in terms of is_convertible<> , which will return false on explicit conversions. 这是有效的,因为(3)是根据is_convertible<>指定的,它将在explicit转换时返回false。 However, that's currently considered a defect and will likely be changed in the future - since after all, we are explicitly constructing every aspect here so the explicit constructors should still be considered! 然而,这当前被认为是一个缺陷,将来可能会发生变化 - 毕竟,我们这里明确地构建了每个方面,所以仍然应该考虑explicit构造函数!

Once that happens, you're kind of out of luck as far as direct copy construction goes. 一旦发生这种情况,就直接复制构建而言,你有点不幸。 You'd have to do something like disable your anything constructor for tuple ? 你必须做一些事情,比如禁用tupleanything构造函数? That seems... not great. 那似乎......不太好。 But in that case, marking that constructor explicit would still work for copy-initialization: 但在这种情况下,标记该构造函数explicit仍然适用于复制初始化:

std::tuple<anything> t2 = t;

which works now even without marking the anything constructor explicit , due to the same aforementioned defect. 由于上述相同的缺陷,即使没有标记anything构造explicit ,它现在也可以工作。

If you look at the implementation of the tuple you'll notice that 如果你看一下元组的实现,你会注意到这一点

_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
    }

The constructor of the tuple passes first argument to the the constructor of the first element of tuple. 元组的构造函数将第一个参数传递给元组的第一个元素的构造函数。 As variable t has type std::tuple<anything> compiler finds the best match for constructing anything with t - a template constructor. 由于变量t具有类型std::tuple<anything>编译器找到与t构造anything的最佳匹配 - 模板构造函数。

To copy a tuple you simply need to write 要复制元组,您只需要编写

std::tuple<anything> t2 = t;

UPD. UPD。

According to standard std::tuple provides following constructors: 根据标准std :: tuple提供以下构造函数:

template <class... UTypes>
explicit constexpr tuple(const Types&...);

template <class... UTypes>
constexpr tuple(const tuple<UTypes...>&);

And apparently your template constructor is a better match than copy constructor of the tuple. 显然你的模板构造函数比元组的复制构造函数更好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM