class my_class
{
...
my_class(my_class const &) = delete;
...
};
What does = delete mean in that context?
Are there any other "modifiers" (other than = 0 and = delete)?
Deleting a function is a C++11 feature:
The common idiom of "prohibiting copying" can now be expressed
directly:
class X {
// ...
X& operator=(const X&) = delete; // Disallow copying
X(const X&) = delete;
};
[...]
The "delete" mechanism can be used for any function. For example, we
can eliminate an undesired conversion like this:
struct Z {
// ...
Z(long long); // can initialize with a long long
Z(long) = delete; // but not anything less
};
= 0 means that a function is pure virtual and you cannot instantiate an object from this class. You need to derive from it and implement this method
= delete means that the compiler will not generate those constructors for you. AFAIK this is only allowed on copy constructor and assignment operator. But I am not too good at the upcoming standard.
This excerpt from The C++ Programming Language [4th Edition] - Bjarne Stroustrup book talks about the real purpose behind using =delete:
3.3.4 Suppressing Operations
Using the default copy or move for a class in a hierarchy is typically
a disaster: given only a pointer to a base, we simply don’t know what
members the derived class has, so we can’t know how to copy
them. So, the best thing to do is usually to delete the default copy
and move operations, that is, to eliminate the default definitions of
those two operations:
class Shape {
public:
Shape(const Shape&) =delete; // no copy operations
Shape& operator=(const Shape&) =delete;
Shape(Shape&&) =delete; // no move operations
Shape& operator=(Shape&&) =delete;
˜Shape();
// ...
};
Now an attempt to copy a Shape will be caught by the compiler.
The =delete mechanism is general, that is, it can be used to suppress any operation
Are there any other "modifiers" (other than = 0 and = delete)?
Since it appears no one else answered this question, I should mention that there is also =default.
https://learn.microsoft.com/en-us/cpp/cpp/explicitly-defaulted-and-deleted-functions#explicitly-defaulted-functions
The coding standards I've worked with have had the following for most of class declarations.
// coding standard: disallow when not used
T(void) = delete; // default ctor (1)
~T(void) = delete; // default dtor (2)
T(const T&) = delete; // copy ctor (3)
T(const T&&) = delete; // move ctor (4)
T& operator= (const T&) = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)
If you use any of these 6, you simply comment out the corresponding line.
Example: class FizzBus require only dtor, and thus do not use the other 5.
// coding standard: disallow when not used
FizzBuzz(void) = delete; // default ctor (1)
// ~FizzBuzz(void); // dtor (2)
FizzBuzz(const FizzBuzz&) = delete; // copy ctor (3)
FizzBuzz& operator= (const FizzBuzz&) = delete; // copy assig (4)
FizzBuzz(const FizzBuzz&&) = delete; // move ctor (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign (6)
We comment out only 1 here, and install the implementation of it else where (probably where the coding standard suggests). The other 5 (of 6) are disallowed with delete.
You can also use '= delete' to disallow implicit promotions of different sized values ... example
// disallow implicit promotions
template <class T> operator T(void) = delete;
template <class T> Vuint64& operator= (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;
A deleted function is implicitly inline
(Addendum to existing answers)
... And a deleted function shall be the first declaration of the function (except for deleting explicit specializations of function templates - deletion should be at the first declaration of the specialization), meaning you cannot declare a function and later delete it, say, at its definition local to a translation unit.
Citing [dcl.fct.def.delete]/4:
A deleted function is implicitly inline. ( Note: The one-definition
rule
([basic.def.odr])
applies to deleted definitions. — end note ] A deleted definition
of a function shall be the first declaration of the function or, for
an explicit specialization of a function template, the first
declaration of that specialization. [ Example:
struct sometype {
sometype();
};
sometype::sometype() = delete; // ill-formed; not first declaration
— end example )
A primary function template with a deleted definition can be specialized
Albeit a general rule of thumb is to avoid specializing function templates as specializations do not participate in the first step of overload resolution, there are arguable some contexts where it can be useful. E.g. when using a non-overloaded primary function template with no definition to match all types which one would not like implicitly converted to an otherwise matching-by-conversion overload; i.e., to implicitly remove a number of implicit-conversion matches by only implementing exact type matches in the explicit specialization of the non-defined, non-overloaded primary function template.
Before the deleted function concept of C++11, one could do this by simply omitting the definition of the primary function template, but this gave obscure undefined reference errors that arguably gave no semantic intent whatsoever from the author of primary function template (intentionally omitted?). If we instead explicitly delete the primary function template, the error messages in case no suitable explicit specialization is found becomes much nicer, and also shows that the omission/deletion of the primary function template's definition was intentional.
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t);
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
//use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}
However, instead of simply omitting a definition for the primary function template above, yielding an obscure undefined reference error when no explicit specialization matches, the primary template definition can be deleted:
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t) = delete;
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
use_only_explicit_specializations(str);
/* error: call to deleted function 'use_only_explicit_specializations'
note: candidate function [with T = std::__1::basic_string<char>] has
been explicitly deleted
void use_only_explicit_specializations(T t) = delete; */
}
Yielding a more more readable error message, where the deletion intent is also clearly visible (where an undefined reference error could lead to the developer thinking this an unthoughtful mistake).
Returning to why would we ever want to use this technique? Again, explicit specializations could be useful to implicitly remove implicit conversions.
#include <cstdint>
#include <iostream>
void warning_at_best(int8_t num) {
std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}
template< typename T >
void only_for_signed(T t) = delete;
template<>
void only_for_signed<int8_t>(int8_t t) {
std::cout << "UB safe! 1 byte, " << +t << "\n";
}
template<>
void only_for_signed<int16_t>(int16_t t) {
std::cout << "UB safe! 2 bytes, " << +t << "\n";
}
int main()
{
const int8_t a = 42;
const uint8_t b = 255U;
const int16_t c = 255;
const float d = 200.F;
warning_at_best(a); // 42
warning_at_best(b); // implementation-defined behaviour, no diagnostic required
warning_at_best(c); // narrowing, -Wconstant-conversion warning
warning_at_best(d); // undefined behaviour!
only_for_signed(a);
only_for_signed(c);
//only_for_signed(b);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = unsigned char]
has been explicitly deleted
void only_for_signed(T t) = delete; */
//only_for_signed(d);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = float]
has been explicitly deleted
void only_for_signed(T t) = delete; */
}
= delete is a feature introduce in C++11. As per =delete it will not allowed to call that function.
In detail.
Suppose in a class.
Class ABC{
Int d;
Public:
ABC& operator= (const ABC& obj) =delete
{
}
};
While calling this function for obj assignment it will not allowed. Means assignment operator is going to restrict to copy from one object to another.
New C++0x standard. Please see section 8.4.3 in the N3242 working draft
This is new thing in C++ 0x standards where you can delete an inherited function.
A small example to summarize some common usages:
class MyClass
{
public:
// Delete copy constructor:
// delete the copy constructor so you cannot copy-construct an object
// of this class from a different object of this class
MyClass(const MyClass&) = delete;
// Delete assignment operator:
// delete the `=` operator (`operator=()` class method) to disable copying
// an object of this class
MyClass& operator=(const MyClass&) = delete;
// Delete constructor with certain types you'd like to
// disallow:
// (Arbitrary example) don't allow constructing from an `int` type. Expect
// `uint64_t` instead.
MyClass(uint64_t);
MyClass(int) = delete;
// "Pure virtual" function:
// `= 0` makes this is a "pure virtual" method which *must* be overridden
// by a child class
uint32_t getVal() = 0;
}
TODO:
I still need to make a more thorough example, and run this to show some usages and output, and their corresponding error messages.
See also
https://www.stroustrup.com/C++11FAQ.html#default - section "control of defaults: default and delete"
Related
class my_class
{
...
my_class(my_class const &) = delete;
...
};
What does = delete mean in that context?
Are there any other "modifiers" (other than = 0 and = delete)?
Deleting a function is a C++11 feature:
The common idiom of "prohibiting copying" can now be expressed
directly:
class X {
// ...
X& operator=(const X&) = delete; // Disallow copying
X(const X&) = delete;
};
[...]
The "delete" mechanism can be used for any function. For example, we
can eliminate an undesired conversion like this:
struct Z {
// ...
Z(long long); // can initialize with a long long
Z(long) = delete; // but not anything less
};
= 0 means that a function is pure virtual and you cannot instantiate an object from this class. You need to derive from it and implement this method
= delete means that the compiler will not generate those constructors for you. AFAIK this is only allowed on copy constructor and assignment operator. But I am not too good at the upcoming standard.
This excerpt from The C++ Programming Language [4th Edition] - Bjarne Stroustrup book talks about the real purpose behind using =delete:
3.3.4 Suppressing Operations
Using the default copy or move for a class in a hierarchy is typically
a disaster: given only a pointer to a base, we simply don’t know what
members the derived class has, so we can’t know how to copy
them. So, the best thing to do is usually to delete the default copy
and move operations, that is, to eliminate the default definitions of
those two operations:
class Shape {
public:
Shape(const Shape&) =delete; // no copy operations
Shape& operator=(const Shape&) =delete;
Shape(Shape&&) =delete; // no move operations
Shape& operator=(Shape&&) =delete;
˜Shape();
// ...
};
Now an attempt to copy a Shape will be caught by the compiler.
The =delete mechanism is general, that is, it can be used to suppress any operation
Are there any other "modifiers" (other than = 0 and = delete)?
Since it appears no one else answered this question, I should mention that there is also =default.
https://learn.microsoft.com/en-us/cpp/cpp/explicitly-defaulted-and-deleted-functions#explicitly-defaulted-functions
The coding standards I've worked with have had the following for most of class declarations.
// coding standard: disallow when not used
T(void) = delete; // default ctor (1)
~T(void) = delete; // default dtor (2)
T(const T&) = delete; // copy ctor (3)
T(const T&&) = delete; // move ctor (4)
T& operator= (const T&) = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)
If you use any of these 6, you simply comment out the corresponding line.
Example: class FizzBus require only dtor, and thus do not use the other 5.
// coding standard: disallow when not used
FizzBuzz(void) = delete; // default ctor (1)
// ~FizzBuzz(void); // dtor (2)
FizzBuzz(const FizzBuzz&) = delete; // copy ctor (3)
FizzBuzz& operator= (const FizzBuzz&) = delete; // copy assig (4)
FizzBuzz(const FizzBuzz&&) = delete; // move ctor (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign (6)
We comment out only 1 here, and install the implementation of it else where (probably where the coding standard suggests). The other 5 (of 6) are disallowed with delete.
You can also use '= delete' to disallow implicit promotions of different sized values ... example
// disallow implicit promotions
template <class T> operator T(void) = delete;
template <class T> Vuint64& operator= (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;
A deleted function is implicitly inline
(Addendum to existing answers)
... And a deleted function shall be the first declaration of the function (except for deleting explicit specializations of function templates - deletion should be at the first declaration of the specialization), meaning you cannot declare a function and later delete it, say, at its definition local to a translation unit.
Citing [dcl.fct.def.delete]/4:
A deleted function is implicitly inline. ( Note: The one-definition
rule
([basic.def.odr])
applies to deleted definitions. — end note ] A deleted definition
of a function shall be the first declaration of the function or, for
an explicit specialization of a function template, the first
declaration of that specialization. [ Example:
struct sometype {
sometype();
};
sometype::sometype() = delete; // ill-formed; not first declaration
— end example )
A primary function template with a deleted definition can be specialized
Albeit a general rule of thumb is to avoid specializing function templates as specializations do not participate in the first step of overload resolution, there are arguable some contexts where it can be useful. E.g. when using a non-overloaded primary function template with no definition to match all types which one would not like implicitly converted to an otherwise matching-by-conversion overload; i.e., to implicitly remove a number of implicit-conversion matches by only implementing exact type matches in the explicit specialization of the non-defined, non-overloaded primary function template.
Before the deleted function concept of C++11, one could do this by simply omitting the definition of the primary function template, but this gave obscure undefined reference errors that arguably gave no semantic intent whatsoever from the author of primary function template (intentionally omitted?). If we instead explicitly delete the primary function template, the error messages in case no suitable explicit specialization is found becomes much nicer, and also shows that the omission/deletion of the primary function template's definition was intentional.
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t);
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
//use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}
However, instead of simply omitting a definition for the primary function template above, yielding an obscure undefined reference error when no explicit specialization matches, the primary template definition can be deleted:
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t) = delete;
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
use_only_explicit_specializations(str);
/* error: call to deleted function 'use_only_explicit_specializations'
note: candidate function [with T = std::__1::basic_string<char>] has
been explicitly deleted
void use_only_explicit_specializations(T t) = delete; */
}
Yielding a more more readable error message, where the deletion intent is also clearly visible (where an undefined reference error could lead to the developer thinking this an unthoughtful mistake).
Returning to why would we ever want to use this technique? Again, explicit specializations could be useful to implicitly remove implicit conversions.
#include <cstdint>
#include <iostream>
void warning_at_best(int8_t num) {
std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}
template< typename T >
void only_for_signed(T t) = delete;
template<>
void only_for_signed<int8_t>(int8_t t) {
std::cout << "UB safe! 1 byte, " << +t << "\n";
}
template<>
void only_for_signed<int16_t>(int16_t t) {
std::cout << "UB safe! 2 bytes, " << +t << "\n";
}
int main()
{
const int8_t a = 42;
const uint8_t b = 255U;
const int16_t c = 255;
const float d = 200.F;
warning_at_best(a); // 42
warning_at_best(b); // implementation-defined behaviour, no diagnostic required
warning_at_best(c); // narrowing, -Wconstant-conversion warning
warning_at_best(d); // undefined behaviour!
only_for_signed(a);
only_for_signed(c);
//only_for_signed(b);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = unsigned char]
has been explicitly deleted
void only_for_signed(T t) = delete; */
//only_for_signed(d);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = float]
has been explicitly deleted
void only_for_signed(T t) = delete; */
}
= delete is a feature introduce in C++11. As per =delete it will not allowed to call that function.
In detail.
Suppose in a class.
Class ABC{
Int d;
Public:
ABC& operator= (const ABC& obj) =delete
{
}
};
While calling this function for obj assignment it will not allowed. Means assignment operator is going to restrict to copy from one object to another.
New C++0x standard. Please see section 8.4.3 in the N3242 working draft
This is new thing in C++ 0x standards where you can delete an inherited function.
A small example to summarize some common usages:
class MyClass
{
public:
// Delete copy constructor:
// delete the copy constructor so you cannot copy-construct an object
// of this class from a different object of this class
MyClass(const MyClass&) = delete;
// Delete assignment operator:
// delete the `=` operator (`operator=()` class method) to disable copying
// an object of this class
MyClass& operator=(const MyClass&) = delete;
// Delete constructor with certain types you'd like to
// disallow:
// (Arbitrary example) don't allow constructing from an `int` type. Expect
// `uint64_t` instead.
MyClass(uint64_t);
MyClass(int) = delete;
// "Pure virtual" function:
// `= 0` makes this is a "pure virtual" method which *must* be overridden
// by a child class
uint32_t getVal() = 0;
}
TODO:
I still need to make a more thorough example, and run this to show some usages and output, and their corresponding error messages.
See also
https://www.stroustrup.com/C++11FAQ.html#default - section "control of defaults: default and delete"
Is it possible for assignment operator to be deduced as a special case of member function template?
For example, I have a class template with one bool parameter and want to implement assign operation regardless any particular value of the template argument.
#include <iostream>
template<bool sw>
struct A {
A() {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
template<bool input_sw>
A & operator = (const A<input_sw> &a) {
std::cout << __PRETTY_FUNCTION__ << '\n';
return *this;
}
};
int main()
{
A<true> a;
A<true> b;
a = b;
}
In the code snippet above clang and gcc-compiled binaries print out nothing about assignment -- as far as I can tell default assignment is generated here despite possibility to deduce it from the template.
It is correct for them to not print anything out. What is happening is that there is an implicit copy assignment operator created for A<true> with this signature:
A<true>& operator=(const A<true>& );
Thus, when we do overload resolution, there are two viable candidates:
A<true>& operator=(const A<true>& ); // implicit copy assignment
A<true>& operator=(const A<input_sw>& ); // [ with input_sw = true ]
Both operators take the same argument (const A<true>&), so they are equivalently good candidates from this perspective. But non template functions are preferred to function template specializations, which makes the implicit copy assignment operator the best viable candidate.
Now, consider an alternative. What if you declared your operator template this way:
template<bool input_sw>
A & operator =(A<input_sw> &a) { ... }
That is, not const. This isn't good practice, and I'm presenting this solely for illustrative purposes. In this case, our two candidates for a=b are:
A<true>& operator=(const A<true>& ); // implicit copy assignment
A<true>& operator=(A<input_sw>& ); // [ with input_sw = true ]
Now the two candidates do not take the same argument. Our operator template takes a reference to non-const. In the case of two references, the one referring to the last cv-qualified type is preferred, which in this case would be the operator. Drop that const, and now your function is preferred, and you will see something printed.
C++ is the best.
I am trying to lean move semantics and I wrote this example. I would like to move a temporary r-value into an object on stack.
class MemoryPage
{
public:
size_t size;
MemoryPage():size(0){
}
MemoryPage& operator= (MemoryPage&& mp_){
std::cout << "2" <<std::endl;
size = mp_.size;
return *this;
}
};
MemoryPage getMemPage()
{
MemoryPage mp;
mp.size = 4;
return mp;
}
int main() {
MemoryPage mp;
mp = getMemPage();
std::cout << mp.size;
return 0;
}
I get this error at the return of getMemPage():
error: use of deleted function 'constexpr MemoryPage::MemoryPage(const MemoryPage&)'
The copy constructor is:
[...] defined as deleted if any of the following conditions are true:
T has a user-defined move constructor or move assignment operator
In order to solve the immediate problem, you simply provide a copy constructor, i.e.:
MemoryPage(const MemoryPage&) { }
However, as noted in the comments, it's a good idea to consult Rule-of-Three becomes Rule-of-Five with C++11? . In particular, this paragraph summarizes what issues you may run into if you neglect to provide any of the special member functions:
Note that:
move constructor and move assignment operator won't be generated for a class that explicitly declares any of the other special member
functions
copy constructor and copy assignment operator won't be generated for a class that explicitly declares a move constructor or move assignment
operator
a class with a explicitly declared destructor and implicitly defined copy constructor or implicitly defined copy assignment operator is
considered deprecated.
formatted for readability
Therefore it is good practice when writing a class that deals with memory management to provide all five special member functions, i.e.:
class C {
C(const C&) = default;
C(C&&) = default;
C& operator=(const C&) & = default;
C& operator=(C&&) & = default;
virtual ~C() { }
};
INTRO
Ref qualifiers : A way to dissambiguate the rl-valuness of the implied object. As a quick example, take the following class
class example
{
int member;
public:
// ...
int& value() &;
// ^
int&& value() &&;
// ^^
int const& value() const&;
// ^
};
The use of this C++11 feature (syntax marked with ^), allows us to control the version of value() that will be called with
l-values
temporaries
const l-values
Practically the ref qualification applies to the classe's *this
Defaulted / Deleted functions : Specify a special member function as having the compiler generated (default) definition or inaccessible (delete). As an example take
struct type {
type(const type&) = delete;
type& operator=(const type&) = delete;
};
The above struct, achieves being non copyable with extremely clear semantics
QUESTIONs
Is it possible / valid to combine these features ?
Which are the cases where it's explicitly forbidden or bad style ?
Is there any use case / pattern for such a combination ? (Eg creating conditional interfaces based rl-valueness quick and easy)
Yes, but there's not much use, as constructors and destructors can't be ref-qualified.
You can ref-qualify assignment operators:
struct S {
S &operator=(const S &) && = default;
};
int main() {
S s;
S() = s; // OK
s = S(); // error - LHS must be an rvalue
}
However, I'm somewhat at a loss to imagine what this would be useful for.
EDIT: solved see comments
--don't know how to mark as solved with out an answer.
After watching a Channel 9 video on Perfect Forwarding / Move semantics in c++0x i was some what led into believing this was a good way to write the new assignment operators.
#include <string>
#include <vector>
#include <iostream>
struct my_type
{
my_type(std::string name_)
: name(name_)
{}
my_type(const my_type&)=default;
my_type(my_type&& other)
{
this->swap(other);
}
my_type &operator=(my_type other)
{
swap(other);
return *this;
}
void swap(my_type &other)
{
name.swap(other.name);
}
private:
std::string name;
void operator=(const my_type&)=delete;
void operator=(my_type&&)=delete;
};
int main()
{
my_type t("hello world");
my_type t1("foo bar");
t=t1;
t=std::move(t1);
}
This should allow both r-values and const& s to assigned to it. By constructing a new object with the appropriate constructor and then swapping the contents with *this. This seems sound to me as no data is copied more than it need to be. And pointer arithmetic is cheap.
However my compiler disagrees. (g++ 4.6) And I get these error.
copyconsttest.cpp: In function ‘int main()’:
copyconsttest.cpp:40:4: error: ambiguous overload for ‘operator=’ in ‘t = t1’
copyconsttest.cpp:40:4: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <near match>
copyconsttest.cpp:31:11: note: no known conversion for argument 1 from ‘my_type’ to ‘my_type&&’
copyconsttest.cpp:41:16: error: ambiguous overload for ‘operator=’ in ‘t = std::move [with _Tp = my_type&, typename std::remove_reference< <template-parameter-1-1> >::type = my_type]((* & t1))’
copyconsttest.cpp:41:16: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <deleted>
Am I doing something wrong? Is this bad practice (I don't think there is way of testing whether you are self assigning)? Is the compiler just not ready yet?
Thanks
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.
Did you closely read the error message? It sees two errors, that you have multiple copy-assignment operators and multiple move-assignment operators. And it's exactly right!
Special members must be specified at most once, no matter if they're defaulted, deleted, conventionally defined, or implicitly handled by being left out. You have two copy-assignment operators (one taking my_type, the other taking my_type const &) and two move-assignment operators (one taking my_type, the other taking my_type &&). Note that the assignment operator that takes my_type can handle lvalue and rvalue references, so it acts as both copy- and move-assignment.
The function signature of most special members have multiple forms. You must pick one; you cannot use an unusual one and then delete the conventional one, because that'll be a double declaration. The compiler will automatically use an unusually-formed special member and won't synthesize a special member with the conventional signature.
(Notice that the errors mention three candidates. For each assignment type, it sees the appropriate deleted method, the method that takes my_type, and then the other deleted method as an emergency near-match.)
Are you supposed to be deleting those overloads of the assignment operator? Shouldn't your declaration of the assignment operator be a template or something? I don't really see how that is supposed to work.
Note that even if that worked, by implementing the move assignment operator that way, the resources held by the object that was just moved from will be released at the time its lifetime ends, and not at the point of the assignment. See here for more details:
http://cpp-next.com/archive/2009/09/your-next-assignment/