开发者

templated constructor vs. templated copy constructor

开发者 https://www.devze.com 2023-01-30 16:35 出处:网络
I have a class with a templated constructor for implicit move conversion, however this constructor should NOT be used for the class (which should only be copy constructible). However, the compiler alw

I have a class with a templated constructor for implicit move conversion, however this constructor should NOT be used for the class (which should only be copy constructible). However, the compiler always tries to use the templated constructor instead of the regular copy constructor.

e.g. With this i get the follow compiler errors, link. (you can just copy paste this code if you want to try it)

struct implementation{};
class my_class
{
 my_class(my_class&&); // delete move-constructor... OUCH... COMPILER ERROR
public:
 my_class(){}
 my_class(const my_class& other) : impl_(other.impl_){}

 template<typename T>
 my_class(T&& impl) : impl_(std::make_shared<T>(std::move(impl))){} // Still tries to use this...

private:
 std::shared_ptr<imp开发者_如何转开发lementation> impl_;
};

class other_class
{
public:
 my_class foo()
 { 
       return instance_; // Wants to use move-constructor???
 }
private:
 my_class instance_;
};

Any one got an idea how to solve this properly?


Okay, here is my complete overhaul of my_class:

class my_class
{
public:
    my_class() {}
    my_class(my_class&& that) : impl_(std::move(that.impl_)) {}

    template <typename T> my_class(T&& impl,
    typename std::enable_if<
        std::is_base_of<
            implementation,
            typename std::remove_reference<T>::type
        >::value,
        void
    >::type* dummy = 0
    ) : impl_(std::make_shared<T>(std::forward<T>(impl))) {}

    template <typename T>
    typename std::enable_if<
        std::is_base_of<
            implementation,
            typename std::remove_reference<T>::type
        >::value,
        my_class&
    >::type
    operator=(T&& impl)
    {
        std::make_shared<implementation>(std::forward<T>(impl)).swap(impl_);
        return *this;
    }

private:
    std::shared_ptr<implementation> impl_;
};

As suggested by others, this works for lvalue and rvalue references by using std::forward instead of std::move. The remove_reference is necessary because for lvalue references, T is a reference, and derived& does not derive from base, but derived does (note the reference).


This can never work:

template<typename T>
my_class(typename std::enable_if<std::is_base_of<implementation, derived_1>::value, T&&>::type impl) : impl_(std::make_shared<T>(std::move(impl)))  {}

template <typename T> 
my_class& operator= (typename std::enable_if<std::is_rvalue_reference<T&&>::value && !std::is_same<T, my_class>::value, T&&>::type impl)

The reason is that you only use T in non-deduced contexts. Plainly speaking, the compiler cannot deduce T if the argument it should have deduce it from has the form Anything<T>::type.

So, if you want to use enable_if on assignment operator, you put it in the return value:

template <class T>
typename enable_if<..., T&>::type operator=(const T&);

in case of the conversion (should work for move, too) constructor, you add a dummy parameter with default value:

template <class T>
MyClass(const T&, typename enable_if<..., void>::type* =0);

FredOverflow already gave you the correct assignment operator.

BTW you needn't restrict it to rvalue-references, if you use std::forward instead of std::move. See here. That would give you (copy&paste from FredOverflow):

template <typename T>
typename std::enable_if<std::is_base_of<implementation, T>::value, my_class&>::type
operator=(T&& impl)
{
    std::make_shared<implementation>(std::forward<T>(impl)).swap(impl_);
    return *this;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消