Be very leery of the copy/swap assignment idiom. It can be sub-optimal, especially when applied without careful analysis. Even if you need strong exception safety for the assignment operator, that functionality can be otherwise obtained.
For your example I recommend:
struct my_type
{
my_type(std::string name_)
: name(std::move(name_))
{}
void swap(my_type &other)
{
name.swap(other.name);
}
private:
std::string name;
};
This will get you implicit copy and move semantics which forward to std::string's copy and move members. And the author of std::string knows best how to get those operations done.
If your compiler does not yet support implicit move generation, but does support defaulted special members, you can do this instead:
struct my_type
{
my_type(std::string name_)
: name(std::move(name_))
{}
my_type(const mytype&) = default;
my_type& operator=(const mytype&) = default;
my_type(mytype&&) = default;
my_type& operator=(mytype&&) = default;
void swap(my_type &other)
{
name.swap(other.name);
}
private:
std::string name;
};
You may also choose to do the above if you simply want to be explicit about your special members.
If you're dealing with a compiler that does not yet support defaulted special members (or implicit move members), then you can explicitly supply what the compiler should eventually default when it becomes fully C++11 conforming:
struct my_type
{
my_type(std::string name_)
: name(std::move(name_))
{}
my_type(const mytype& other)
: name(other.name) {}
my_type& operator=(const mytype& other)
{
name = other.name;
return *this;
}
my_type(mytype&& other)
: name(std::move(other.name)) {}
my_type& operator=(mytype&& other)
{
name = std::move(other.name);
return *this;
}
void swap(my_type &other)
{
name.swap(other.name);
}
private:
std::string name;
};
If you really need strong exception safety for assignment, design it once and be explicit about it (edit to include suggestion by Luc Danton):
template <class C>
typename std::enable_if
<
std::is_nothrow_move_assignable<C>::value,
C&
>::type
strong_assign(C& c, C other)
{
c = std::move(other);
return c;
}
template <class C>
typename std::enable_if
<
!std::is_nothrow_move_assignable<C>::value,
C&
>::type
strong_assign(C& c, C other)
{
using std::swap;
static_assert(std::is_nothrow_swappable_v<C>, // C++17 only
"Not safe if you move other into this function");
swap(c, other);
return c;
}
Now your clients can choose between efficiency (my type::operator=), or strong exception safety using strong_assign
.