Replacing Implicitly-Deleted Operator With Template Method [duplicate] - c++

This question already has answers here:
Template assignment operator doesn't replace the default assignment operator
(2 answers)
Template assignment operator overloading mystery
(2 answers)
Closed 4 years ago.
Consider the following code:
class Foo { public:
int const i;
Foo() : i(0) {}
};
void test() {
Foo a;
Foo const b;
a = b; //Failure here; `Foo::operator=` implicitly deleted
}
The indicated line fails because the operator= ordinarily generated by the compiler cannot be generated: .i is marked const. This is good and expected.
The programmer can generate their own operator=, so long as it does not change .i. E.g. this method does nothing:
Foo& operator=(Foo const& other) {
return *this;
}
But now, suppose I want to be more-general. I templatize the method:
template <typename TypeOther>
Foo& operator=(TypeOther const& other) {
return *this;
}
Suddenly, I get back the original failing behavior (tested Clang, GCC, and MSVC)! It seems that, even though the default operator= is not generated, it participates in overload resolution and therefore is selected instead of the template variant.
Why is the implicit operator is being selected even though it does not exist (i.e, is it as if I had just written and deleted it myself—that is, it exists but is forbidden)?
How I can work around this to make my template operator= be selected for any type?

Related

C++ - Unable to overload assignment operator [duplicate]

This question already has answers here:
The assignment operator and initialization
(2 answers)
Closed 6 years ago.
I am trying to overload the assignment operator but it doesn't seem to work. The code is based on this answer. I've searched for other examples of overloading the assignment operator, but it doesn't seem like my code shouldn't run.
This is my code:
#pragma once
#include <assert.h>
class ReadOnlyInt
{
public:
ReadOnlyInt(): assigned(false) {}
ReadOnlyInt& operator=(int v);
operator int() const ;
private:
int value;
bool assigned;
};
ReadOnlyInt& ReadOnlyInt::operator=(int v)
{
assert(!assigned);
value = v;
assigned = true;
return *this;
}
ReadOnlyInt::operator int() const
{
assert(assigned);
return value;
}
Intellisense doesn't give any warnings, but the operator= is not highlighted as a keyword.
Now if I make an assigment, Intellisense does recognize it's not possible:
ReadOnlyInt bar = 12;
no suitable constructor exists to convert from "int" to "ReadOnlyInt"
This works however:
int foo = bar;
Solution
This question was marked as duplicate, so I can't answer it. This was the solution I came up with based on the comments and answer on this question:
ReadOnlyInt::ReadOnlyInt()
: assigned(false)
{}
ReadOnlyInt::ReadOnlyInt(int v)
: value(v), assigned(true)
{}
You can't initialize and declare at the same time. You need to do this
ReadOnlyInt bar;
bar = 12;
This is because there is no appropriate constructor for ReadOnlyInt that takes an int argument.

Non-friend single-line operator + overload based on operator +=

Let's say I have a class:
class A
{
//...
};
With well defined operator +=:
A& operator +=(const A&) {return *this;} //class without members
So let's try to overload operator+ also (as non-friend). I don't want to use class name to call constructor of temporary object (kinda want this make generic code):
A operator +(const A& other) const
{
return auto(*this)(*this) += other; //error: invalid use of auto
// /\/\/\/\/\ /\
// type of *this ||
// constructor call
}
auto is no good here. Let's try decltype.
A operator +(const A& other) const
{
return decltype(*this)(*this) += other; //error: 'A& A::operator+=(const A&)' discards
// /\/\/\/\/\/\/\ /\ qualifiers [-fpermissive] return
// type of *this || decltype(*this)(*this) += other;
// constructor call ^
}
This managed to get the type out of *this, but operator+ is declared const, so we got const A deduced (that's what I thought). So let's go on:
A operator +(const A& other) const
{
return typename std::remove_const<decltype(*this)>::type(*this) += amount;
//same error as previous
}
Now I got mindblown. Even thought I removed constness, it still discards
qualifier. Well, maybe that's because all I was doing was just CASTING. So stupid. To call a constructor I would have to generate code that (besides type) has ::Constructor (so I even tried to make an alias for constructor, but at this point I failed so hard). My broken brain gave up, but rest of my consciousness gave me an solution (which is generic in writing, so that's good):
// outside of class
template<class A>
inline A&& make_rvalue(A copy)
{
return std::move(copy);
}
// inside of class
A operator +(const A& other) const
{
return make_rvalue(*this) += other; // such generic code
}
That's what I ended with. But is there some trick that doesn't involve any other function call?
EDIT: I know classic methods of doing this, but what I search is described below:
operator is reduced to {return /*...*/;}
doesn't involve names of class methods or global functions
takes to account overloads with other types - it cannot take argument(s) by value, since class A != class B, argument int over const int& doesn't help much with Matrix class (but proxy operator that calls target operator with exchanged arguments is OK)
takes to account (possible) order of operation (x#y != y#x), where both should should have same return statement
return statement should be exacly the same for given operator# is every class that has overloaded operator +=
If you create a function like this:
template <typename T>
T add(T temp,const T &b)
{
temp += b;
return temp;
}
You can use it like this:
A operator+(const A& other) const { return add(*this,other); }
I'm going to go on record as saying this whole line of thinking/coding is going the wrong way. You already know that (in this case) the return type is A. Your code seems to be putting a lot of work into saying A in the most complex, indirect way possible.
A operator +(const A& other) const {
A ret {*this};
ret += other;
return ret;
}
Since you seem to really want to use C++11 features (even though in this case almost none of them provides any real help) I used a braced initializer, so it uses something from C++11. And no, this isn't one line, but it is readable.
Note that the same style is fine in generic code as well. For example, in a non-member overload of operator+, we might have something like this:
template <class T>
T operator+(T const &a, T const &b) {
T ret {a};
ret += b;
return ret;
}
This can be simplified a bit as well though:
template <class T>
T operator+(T ret, t const &b) {
ret += b;
return ret;
}
Again, we lose precisely nothing from the fact that older compilers can and will accept the code without problem.

Compile error "No global operator found" for templated overloaded operator

I've defined a template class and overloaded operators. When compiling, I get the following error message:
error C2677: binary '+=' : no global operator found which takes type 'Class' (or there is no acceptable conversion)
Here is the relevant code of the class:
template<int foo>
class Class
{
private:
int value;
public:
template<int other_foo>
friend class Class;
// Constructors and copy constructors for several data type
// including other possibilities of "foo"; all tested.
Class& operator+=(const Class& x)
{
value += x.value;
return *this;
}
template<class T>
Class& operator+=(const T& x)
{
this += Class(x);
return *this;
}
};
If I create two objects of, e.g., Class<3>; the operator += works fine and does the right thing.
However, if I have an object of Class<3> and one of Class<2>, I get the above error which points at the line where "+=" is defined for T [the constructor for different value of foo works fine and is also tested].
What am I doing wrong? How can I resolve this error? The operator is defined, just a few lines above.
Assuming that the necessary constructor does indeed exist and work correctly, the error in the code you've posted is
this += Class(x);
which tries to modify the value of the immutable pointer this. It should be
*this += Class(x);
I think that there are two problems, both in this line:
this += Class(x);
One: the object added to should be *this instead of this, because the latter is a pointer, not the object itself.
Two: There is no conversion constructor from T to Class. That is, you cannot convert from Class<3> to Class<2> so the Class(x) will not compile. The solution would be to add it:
template<int other> Class(const Class<other> &o)
{}

Copy Constructor invoked instead of assignment operator

Consider the following self contained Code.
#include <iostream>
template<typename Ty>
class Foo {
private:
Ty m_data;
public:
Foo() :m_data() {}
Foo(Ty data) :m_data(data) {}
template<typename U>
Foo& operator=(Foo<U> rv)
{
m_data = rv.m_data;
return *this;
}
private:
Foo(Foo&);
Foo& operator=(Foo&);
};
int main()
{
Foo<int> na(10);
Foo<int> nb;
nb = Foo<int>(10); // (1)
Foo<int>(10); // (2)
}
My understanding is statement (1) is an assignment rather than a Copy COnstructor. Yet, when compiling (VC++ and G++), the Error Message states, it tries to match a Copy Constructor which was declared private.
1>Source.cpp(23): error C2248: 'Foo<int>::Foo' : cannot access private member declared in class 'Foo<int>'
1> Source.cpp(16) : see declaration of 'Foo<int>::Foo'
My question is, why does it try to search for a Copy Constructor instead of an assignment.
Note, I know it is the assignment that is failing because (2) compiles fine without any error.
Your assignment operator takes its parameter by value, which requires making a copy. That copy may (or may not) be elided - but the copy constructor still needs to be available and accessible, even if not called.
There are two issues:
Your private assignment operator Foo& operator=(Foo&); takes a non-const lvalue reference. That means it cannot be selected as an overload in nb = Foo<int>(10);, because the RHS is an rvalue
That leads to your template assignment operator being selected. But that takes its argument by value, requiring a copy or move copy constructor.
If you fix 1. to take a const reference, gcc gives the following error:
error: 'Foo& Foo::operator=(const Foo&) [with Ty = int]' is private
If you fix 2. so that the template assignment operator takes a const reference, the code compiles without errors.
Your assignment operator passes argument by value, so it uses copy ctor:
template<typename U>
Foo& operator=(Foo<U> rv)
Possible solution to pass it by const refernce:
template<typename U>
Foo& operator=(const Foo<U> &rv)
A private version
private:
Foo(Foo&);
Foo& operator=(Foo&);
cannot be called because it takes non-const lvalue reference so
Foo& operator=(Foo<U> rv)
this version is called but it takes parameter by value and copy constructor has to be invoked.

Overload an operator twice [duplicate]

This question already has answers here:
Overloading by return type
(11 answers)
Closed 8 years ago.
It's possible to overload the same operator twice on C++?
When I try to overload the + operator using the return type as a base, the compiler show me an error.
bigint.h:41:9: error: ‘std::string BigInt::operator+(BigInt)’ cannot be overloaded
bigint.h:40:9: error: with ‘BigInt BigInt::operator+(BigInt)’
This is my code:
.h:
BigInt operator + (BigInt);
string operator + (BigInt);
.cc:
BigInt BigInt::operator + (BigInt M){
if (this->number.size() != M.number.size())
fixLength (this->number, M.number);
// Call Sum;
this->number = Sum (this->number, M.number);
return (*this);
}
string BigInt::operator + (Bigint M){
// Call BigInt overload +;
}
Edit: Apparently I cannot overload the same operator twice using the return type as a base. Suggestions?
As has been pointed out, you cannot overload beased on return type alone. So this is fine:
Foo operator+(const Foo&, const Foo&);
Foo operator+(const char*, double);
but this is not:
Foo operator+(const Foo&, const Foo&);
Bar operator+(const Foo&, const Foo&);
But most of the time there are valid and simple solutions to a given problem. For instance, in a situation like yours, where you want the following to work:
Foo a, b;
Foo c = a + b;
Bar bar = a + b;
then a common strategy is to either give Bar an implicit converting constructor:
struct Bar
{
Bar(const Foo& foo) { .... }
};
or give Foo a conversion operator:
struct Foo
{
explicit operator Bar() { .... }
....
};
Note you can't mark the operator explicit if you don't have a C++11 compiler.
method overload in C++ is by argument list, not the return value.. so in your case, the two methods are ambiguous and the compiler can't tell which one to use (they have the same argument list)
No, you can't overload based on the return type.
From standard docs., Sec 13.1.2,
Function declarations that differ only in the return type cannot be overloaded.
What this means is that methods can be overloading only if they differ by parameters. As with C++, a method's return type is not considered part of the method signature.
Check Wiki for Name Mangling for more details