Conflict between copy constructor and forwarding constructor

前端 未结 3 461
面向向阳花
面向向阳花 2020-12-01 19:03

This problem is based on code that works for me on GCC-4.6 but not for another user with CLang-3.0, both in C++0x mode.

template 
struct My         


        
3条回答
  •  北海茫月
    2020-12-01 19:24

    I've personally had the problem with GCC snapshots for quite some time now. I've had trouble figuring out what was going on (and if it was allowed at all) but I came to a similar conclusion as Dietmar Kühl: the copy/move constructors are still here, but are not always preferred through the mechanics of overload resolution.

    I've been using this to get around the problem for some time now:

    // I don't use std::decay on purpose but it shouldn't matter
    template
    using is_related = std::is_same<
        typename std::remove_cv::type>::type
        , typename std::remove_cv::type>::type
    >;
    
    template
    struct enable_if_unrelated: std::enable_if {};
    
    template
    struct enable_if_unrelated
    : std::enable_if::value> {};
    

    Using it with a constructor like yours would look like:

    template<
        typename... Args
        , typename = typename enable_if_unrelated::type
    >
    MyBase(Args&&... args);
    

    Some explanations are in order. is_related is a run off the mill binary trait that checks that two types are identical regardless of top-level specifiers (const, volatile, &, &&). The idea is that the constructors that will be guarded by this trait are 'converting' constructors and are not designed to deal with parameters of the class type itself, but only if that parameter is in the first position. A construction with parameters e.g. (std::allocator_arg_t, MyBase) would be fine.

    Now I used to have enable_if_unrelated as a binary metafunction, too, but since it's very convenient to have perfectly-forwarding variadic constructors work in the nullary case too I redesigned it to accept any number of arguments (although it could be designed to accept at least one argument, the class type of the constructor we're guarding). This means that in our case if the constructor is called with no argument it is not SFINAE'd out. Otherwise, you'd need to add a MyBase() = default; declaration.

    Finally, if the constructor is forwarding to a base another alternative is to inherit the constructor of that base instead (i.e. using Base::Base;). This is not the case in your example.

提交回复
热议问题