Objects that can be initialized but not assigned - c++

I need to create a class whose objects can be initialized but not assigned.
I thought maybe I could do this by not defining the assignment operator, but the compiler uses the constructor to do the assignment.
I need it to be this way:
Object a=1; // OK
a=1; // Error
How can I do it?

Making a const will do the trick
const Object a=1; // OK
Now you won't be able to assign any value to a as a is declared as const. Note that if you declare a as const, it is necessary to initialize a at the time of declaration.
Once you have declared a as const and also initialized it, you won't be able to assign any other value to a
a=1; //error

You can delete the assignment operator:
#include <iostream>
using namespace std;
struct Object
{
Object(int) {}
Object& operator=(int) = delete;
};
int main()
{
Object a=1; // OK
a=1; // Error
}
Alternative Solution
You can use the explicit keyword:
#include <iostream>
using namespace std;
struct Object
{
explicit Object(int) {}
};
int main()
{
Object a(1); // OK - Uses explicit constructor
a=1; // Error
}
Update
As mentioned by user2079303 in the comments:
It might be worth mentioning that the alternative solution does not prevent regular copy/move assignment like a=Object(1)
This can be avoided by using: Object& operator=(const Object&) = delete;

I hoped this would be so by not defining the assignment operator
This doesn't work because the copy assignment operator (which takes const Object& as parameter) is implicitly generated. And when you write a = 1, the generated copy assignment operator will be tried to invoke, and 1 could be implicitly converted to Object via converting constructor Object::Object(int); then a = 1; works fine.
You can declare the assignment operator taking int as deleted (since C++11) explicitly; which will be selected prior to the copy assignment operator in overload resolution.
If the function is overloaded, overload resolution takes place first, and the program is only ill-formed if the deleted function was selected.
e.g.
struct Object {
Object(int) {}
Object& operator=(int) = delete;
};
There're also some other solutions with side effects. You can declare Object::Object(int) as explicit to prohibit the implicit conversion from int to Object and then make a = 1 fail. But note this will make Object a = 1; fail too because copy initialization doesn't consider explicit constructor. Or you can mark the copy assignment operator deleted too, but this will make the assignment between Objects fail too.

How can I do it?
Option 1:
Make the constructor explicit
struct Object
{
explicit Object(int in) {}
};
Option 2:
delete the assignment operator.
struct Object
{
Object(int in) {}
Object& operator=(int in) = delete;
};
You can use both of the above options.
struct Object
{
explicit Object(int in) {}
Object& operator=(int in) = delete;
};
Option 3:
If you don't want any assignment after initialization, you can delete the assignment operator with Object as argument type.
struct Object
{
explicit Object(int in) {}
Object& operator=(Object const& in) = delete;
};
That will prevent use of:
Object a(1);
a = Object(2); // Error
a = 2; // Error

Deleted functions are available only from C++11 onwards, for older compilers you can make the assignment operator private.
struct Object
{
Object(int) {}
private:
Object& operator=(int);
};
Compiler will now throw error for
Object a=1; //ok
a=2; // error
But you can still do
Object a=1,b=2;
b=a;
Because the default assignment operator is not prevented from being generated by the compiler. So marking default assignment private will solve this issue.
struct Object
{
Object(int) {}
private:
Object& operator=(Object&);
};

Related

Differences between assignment constructor and others in C++

Consider:
std::shared_ptr<Res> ptr=new Res();
The above statement doesn't work. The compiler complains there isn't any viable conversion....
While the below works
std::shared_ptr<Res> ptr{new Res()} ;
How is it possible?!
Actually, what are the main differences in the constructor{uniform, parentheses, assignments}?
The constructor of std::shared_ptr taking a raw pointer is marked as explicit; that's why = does not allow you to invoke this constructor.
Using std::shared_ptr<Res> ptr{new Res()}; is syntax that allows you to call the an explicit constructor to initialize the variable.
std::shared_ptr<Res> ptr=new Res();
would invoke an implicit constructor taking a pointer to Res as single parameter, but not an explicit one.
Here's a simplified example using a custom class with no assignment operators and just a single constructor:
class Test
{
public:
Test(int i) { }
// mark all automatically generated constructors and assignment operators as deleted
Test(Test&&) = delete;
Test& operator=(Test&&) = delete;
};
int main()
{
Test t = 1;
}
Now change the constructor to
explicit Test(int i) { }
and the main function will no longer compile; the following alternatives would still be viable:
Test t(1);
Test t2{1};

E1776 upon assignment (overloaded) with implicit cast access

Suppose I have the code below, where the copy-assignment operator is deleted and an int-assignment operator is emplaced alongside an int-access operator (not marked with the explicit keyword). The assignment of b to a only works when explicitly casting to int as below, while a simple a = b; generates a compilation error of E1776 function "OverloadTest::operator=(const OverloadTest &)" cannot be referenced -- it is a deleted function. Is there any explanation for this behavior, which ought to take advantage of the explicit deletion and the implementation of implicit operators? Using MSVC++ 14.15.
class OverloadTest
{
int i;
public:
OverloadTest(int i) : i(i)
{
}
OverloadTest operator=(const OverloadTest &) = delete;
int operator=(const int &other)
{
i = other;
return i;
}
operator int()
{
return i;
}
};
int main()
{
OverloadTest a(1), b(2);
a = b; // E1776
a = (int)b; // OK
int (OverloadTest::* e)(const int &) = &OverloadTest::operator=;
(a.*(&OverloadTest::operator=))(b); // E0299
(a.*e)(b); // OK
return 0;
}
Actually it is not really clear why you expected something else as this is just how deleting a method is supposed to work. From cppreference (emphasize mine):
If, instead of a function body, the special syntax = delete ; is used,
the function is defined as deleted. Any use of a deleted function
is ill-formed (the program will not compile).
By writing
OverloadTest operator=(const OverloadTest &) = delete;
you do define the operator, but calling it will make your code ill-formed. I find it difficult to answer more than that, because your example is rather academic. You can make a=b; work if you simple do not declare the operator=(const OverloadTest&) at all. However, note that then the compiler generated operator= will be used to evaluate a=b;. Though as your class only has the int member you actually cannot tell the difference between calling that operator or your conversion followed by operator=(int). Hope that helps.

default copy assignment operator doesn't pass the is_copy_assignable test

#include <type_traits>
struct A {};
struct B {
B(A& a) : a(a) {}
//B& operator=(B const& b) = default; // LINE 1
//B& operator=(B const& b) { a = b.a; return *this; } // LINE 2
A& a;
};
static_assert(std::is_copy_constructible<B>::value, ""); // pass
static_assert(std::is_copy_assignable<B>::value, ""); // fail
int main() {
A a;
B b(a);
return 0;
}
Compiler: g++ 5.1
The default copy constructor is fine, but the default copy assignment operator fails (even with LINE 1 uncommented).
Declaring it explicitly (uncommenting LINE 2) works though. Is it a bug?
No, it's not a bug. Your class doesn't have a defined copy assignment operator, because you have a reference member. If you have a class with reference semantics, it is not obvious whether you would want assignment to just assign from one reference to another (which results in copy assigning the referenced object), or whether the reference should be rebound (something you can't actually do, although this is usually what you would want). So the standard simply doesn't generate a default assignment operator, but allows you to define one manually with the semantics you want.

moving temporary value using rvalue reference

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() { }
};

c++ deleted move assignment operator compilation issues

The following code fails with gcc 4.8.0 (mingw-w64) with -O2 -std=c++11 -frtti -fexceptions -mthreads
#include <string>
class Param
{
public:
Param() : data(new std::string) { }
Param(const std::string & other) : data(new std::string(other)) { }
Param(const Param & other) : data(new std::string(*other.data)) { }
Param & operator=(const Param & other) {
*data = *other.data; return *this;
}
~Param() {
delete data;
}
Param & operator=(Param &&) = delete;
private:
std::string * data;
};
int main()
{
Param param;
param = Param("hop");
return 0;
}
With the error : error: use of deleted function 'Param& Param::operator=(Param&&)'
On the line :
param = Param("hop");
And compiles well if I remove the move assignment delete line.
There should be no default move assignment operator since there are user defined copy constructors, user defined copy assignment, and destructors, so deleting it should not affect the compilation, why is it failing?
And why is the allocation simply not using a copy assignment?
The function you deleted is exactly the assignment operator you try to use in main. By explicitly defining it as deleted you declare it and at the same time say using it is an error. So when you try to assign from an rvalue (Param("hop")), the compiler first looks whether a move assignment operator was declared. Since it was and is the best match, it tries to use it, just to find that it was deleted. Thus the error.
Here's another example of this mechanism which uses no special functions:
class X
{
void f(int) {}
void f(short) = delete;
};
int main()
{
X x;
short s;
x.f(s); // error: f(short) is deleted.
}
Removing the deleted f(short) will cause the compiler to select the non-deleted f(int) and thus compile without error.