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;
}
精彩评论