When I try to compile the following (g++ 4.6.3)
class A {};
A& operator*=( A& a, const A& b )
{
return a;
}
A operator*( const A& a, const A& b )
{
return A( a ) *= b;
}
int main( int, char*[] )
{
A a, b;
a = a*b;
return 0;
}
I get the error
/tmp/test.cxx: In function ‘A operator*(const A&, const A&)’:
/tmp/test.cxx:14:20: error: no match for ‘operator*=’ in ‘(* & a) *= b’
/tmp/test.cxx:14:20: note: candidate is:
/tmp/test.cxx:6:1: note: A& operator*=(A&, const A&)
/tmp/test.cxx:6:1: note: no known conversion for argument 1 from ‘A’ to ‘A&’
This puzzles me - how can a conversion from a class to a reference to that class not be known?
Changing the declaration of class A as follows does not have any effect:
class A
{
public:
A() {}
A( const A& ) {}
};
Same error.
I would be extremely grateful for hints as to what's going on here.
Like Lucian said, you cannot bind a temporary object to a non-const reference. The expectance of the compiler is that the object will cease to exist after the expression so it makes no sense to modify it.
To fix your code, remove the temporary (making the argument const& makes no sense in operator *=):
A operator*(A a, const A& b)
{
return a *= b;
}
When you write A( a ), you create a temporary of type A ( a rvalue ) that you copy-construct with a. C++ states that no rvalue could be passed as non const reference. Visual Studio is a bit sloppy about this rule, but gcc and the like enforce it.
To fix, try this ( which is exactly the same, but you create a lvalue by naming that variable ). More on l- and r-value here
A operator*( A a, const A& b )
{
return a *= b;
}
Related
I am refactoring our code-base, where I have the following code (simplified):
template <typename T>
class TVector3;
template <typename T>
class TVector4;
template <typename T>
struct TVector4
{
TVector3<T>& V3() { return (TVector3<T> &) *this; }
const TVector3<T>& V3() const { return (const TVector3<T> &) *this; }
};
template <typename T>
struct TVector3
{
template <typename U>
constexpr TVector3(const TVector4<U>& v) noexcept { }
};
typedef TVector3<float> Vec3f;
typedef TVector4<float> Vec4f;
struct RGBA
{
Vec4f rgba;
operator Vec3f() const { return rgba.V3(); }
};
clang warns me about returning reference to local temporary object (https://godbolt.org/z/ccxbjv771). Apparently (const TVector3<T> &) *this results in calling TVector3(const TVector4<U>& ), but why ?
Intuitively, I would have expected (const TVector3<T> &) to behave like reinterpret cast, or if at least the cast would have looked like (const TVector3<T>) *this (without &) it would kind of make sense to me that the compiler picked that constructor call for conversion.
Another simpler example:
#include <iostream>
struct A { };
struct B
{
B(const A& ) { std::cout << "B(const A&)" << std::endl; }
};
int main()
{
A a;
(const B&) a;
return 0;
}
It prints B(const A&) (https://godbolt.org/z/ocWjh1eb3), but why ? I am converting to const B& and not to B.
It calls the constructor because such are the rules of c-style cast:
cppreference: When the C-style cast expression is encountered, the compiler attempts to interpret it as the following cast expressions, in this order:
a) const_cast<new_type>(expression);
b) static_cast<new_type>(expression), with extensions: pointer or reference to a derived class is additionally allowed to be cast to pointer or reference to unambiguous base class (and vice versa) even if the base class is inaccessible (that is, this cast ignores the private inheritance specifier). Same applies to casting pointer to member to pointer to member of unambiguous non-virtual base;
c) static_cast (with extensions) followed by const_cast;
d) reinterpret_cast<new_type>(expression);
e) reinterpret_cast followed by const_cast.
The first choice that satisfies the requirements of the respective cast operator is selected, even if it cannot be compiled (see example). If the cast can be interpreted in more than one way as static_cast followed by a const_cast, it cannot be compiled.
static_cast is chosen because it considers the constructors. Please do not use c-style cast, you see the rules are not so easy and it forced you to make a question about them.
Intuitively, I would have expected (const TVector3 &) to behave like reinterpret cast
You wouldn't want that, it would break strict aliasing. But if you remove the constructor, it will happily do that as per d).
#include <iostream>
struct A { };
struct B
{
B(const A& ) { std::cout << "B(const A&)" << std::endl; }
};
struct C
{
};
int main()
{
A a;
const B& temp1 = (B&) a;// Ctor, copy, prolonged-life good.
const B& temp2 = (const B&) a;// Ctor, copy, prolonged-life good.
B& dangling_temp = (B&) a;// Ctor, copy, no prolongment->dangling ref, BAD.
(const C&) a;// REINTERPET_CAST
//(const C) a;// Compiler error, good.
(const C*) &a;// REINTERPET_CAST
return 0;
}
But a is not a B? If you really really want it to be B, use reinterpret_cast(but don't) or bit_cast explicitly. The sane thing is to attempt to make a copy if possible. It creates a new temporary B and binds it to const B&. Had you stored it into const B& b, it would prolong the temporary's lifetime, making the code safe.
It prints B(const A&), but why ? I am converting to const B& and not to B.
The type of a is A, it can't be bound to const B& directly. It needs to be converted to B via B::B(const A& ) firstly; then the converted temporary B is bound to const B&. (Lvalue-reference to const could bind to temporaries.)
I try to use boost::in_place for the non-movable and non-copyable object which constructor takes other object by the reference:
struct A
{
};
struct B
{
B(A& a): a_(a){}
B(B const &) = delete;
B(B&&) = delete;
B& operator=(B const &) = delete;
B& operator=(B&) = delete;
A& a_;
};
int main()
{
A a;
boost::optional<B> op(boost::in_place(a));
return 0;
}
The code doesn't compile: binding reference of type ‘A&’ to ‘const A’ discards qualifiers
How to fix that ?
Use the inplace constructor.
In boost, this is this constructor, that takes a in_place_init_t variable, then constructs in place with the following arguments.
boost::optional<B> op(boost::in_place_init, a);
// Calls `B::B(A&) with `a`
Or to continue using in_place, which by default takes a const reference, specify that it isn't a const reference:
boost::optional<B> op(boost::in_place<A&>(a));
In the following example:
class A {
private: double content;
public:
A():content(0) {}
A operator+(const A& other) {
content += other.content;
return *this;
}
void operator=(const A& other) {
content = other.content;
}
};
A is a simple wrapper for a double for which the + and = operators have been overloaded. In the following use:
int main(int argc, char *argv[]) {
A a, b, c;
(a+b) = c ; // Why is this operation legal?
}
Why does (a+b) = c compile? I would like to know why this statement is legal, because the result of (a+b) must be an rvalue. I am not returning a reference from operator+.
(a+b) = c is the same as (a+b).operator=(c). There is no special rule for rvalue references in assignment operators, it just follows usual function call rules. If you want to prevent calling with rvalues, you can add a ref-qualifier:
void operator= (const A& other) & {
// ^
content = other.content;
}
This will only allow the function to be called on lvalues.
class A
{
public:
int v;
A * p;
A& operator*(const A& a)
{
return this->v*a.v// here is a red line under this say error initial value of reference to non-const must be an value
}
~A()
{
this;
}
};
int main()
{
A a;
a.p = new A;
delete a.p;
return 0;
system("pause");
return 0;
}
overloading * operator I cannot use this to represent the object itself. Why this happened.
Surely it says that it must be an lvalue. You're trying to return a reference to a temporary. This is bad karma.
Besides, it's not at all what you want. The multiplication operator should definitely return a value, not a reference.
Not sure what your constructor looks like, but assuming it takes an integer:
A operator * (A const& other) const
{
return A{ v * other.v};
};
Edit:
And actually you should go a step further:
struct A
{
A& operator *= (A const& other) { v *= other.v; return *this; }
A(int i) : v(i) {}
private:
int v;
}
A operator * (A lh, A const& rh)
{
A res{std::move(lh)};
res *= rh;
return res;
}
this->v*a.v evaluates to an int. An int cannot be converted to an A&.
Use
A operator*(const A& a) // Return a value, not a reference.
{
A res;
res.v = this->v*a.v;
return res;
}
You should make the member function a const member function too since it does not modify the object.
A operator*(const A& a) const
{
A res;
res.v = this->v*a.v;
return res;
}
The result of this->v * a.v is an rvalue, a temporary unnamed value. As a temporary, it cannot bind to a non-const reference. Only lvalues can bind to non-const references. That's what the error "initial value of reference to non-const must be an lvalue" is referring to.
However, you don't want to return a const reference either, you want to return the value by value:
A operator*(const A& a) { … }
^ remove &
Note: this will not fix your code completely as you're trying to return an int where you declared to return an A, and int isn't implicitly convertible to A in your current code.
Consider the following code:
class A
{
public:
A& operator=( const A& );
const A& operator+( const A& );
const A& operator+( int m );
};
int main()
{
A a;
a = ( a + a ) + 5; // error: binary '+' : no operator found which takes a left-hand operand of type 'const A'
}
Can anyone explain why the above is returned as an error?
"( a + a )" calls "const A& operator+( const A& )" and returns a constant reference which is then passed to "const A& operator+( int m )" if I'm not mistaken.
How can one fix the above error (without creating a global binary operator+ or a constructor that accepts an int) such that the statement inside main() is allowed?
which is then passed to "const A& operator+( int m )" if I'm not mistaken
No. Since the LHS is a const A& and RHS is an int, it will call*
[anyType] operator+ (int rhs) const
// ^^^^^ note the const here.
as you've only provided the non-const version const A& operator+( int m ), the compiler will complain.
*: Or operator+(const int& rhs) const or operator+(float rhs) const... The crucial point is that it must be a const method.
operator+ should return an instance, not a reference:
// as member function
A operator+(const A& other);
// as free function
A operator+(const A& left, const A& right);
To explain the specific problem is "returns a constant reference which is then passed to const A& operator+( int m )". Since you have a const reference, it cannot call that function because it's not a const method (i.e. const A& operator+( int m ) const).
That said, that is not the way to fix operator+. If you're returning a reference, what is it a reference to? A local in operator+ would be bad as you shouldn't return a reference to a local. A reference to a global would be bad because it will limit how your code can be used properly. A reference to allocated memory would be bad because it will leak memory. A reference to *this would be bad because then operator+ is acting like operator +=.
Because you are modifying the left-hand side object when adding. You can't do that with a const object. Take Samuel advice btw, because the idiomatic way is to return a new copy of the added objects.
The function needs to be const:
const A& operator+( int m ) const;
As const A& operator+( const A& ) returns a const reference a non-const member function const A& operator+( int m ) can not be called over a const object.
Either, the first operator should be defined as A& operator+( const A& ) or, the second operator as const A& operator+( int m )const;
However, these changes will only make them technically correct, not aesthetically in majority of the cases, as a binary operator is not supposed to modify any of the input argument and yet to compute a result and return. Thus the result have to be returned by value or in case of C++0x , as a r-value reference.
i.e A operator+(const A& rhs)const or A&& operator+(const A& rhs)const;
The problem is that (a+a) returns a so-called rvalue (basically a fancy term for a temporary). While you can invoke member functions on an rvalue, you can only invoke const member functions. Also, everyone is right in saying that operator+ must alwasys return a new value.
Your operators should be implemented like this:
A operator+( const A& ) const;
A operator+( int m ) const;
However, binary operators which don't modify their left argument are probably better implemented as free functions:
class A { ... };
A operator+(const A& lhs, const A& rhs);
A operator+(const A& lhs, int rhs);
Usually, they are implemented on top of operator+=, which is implemented as a member:
class A {
public:
A& operator+=(const A& rhs);
A& operator+=(int rhs);
};
inline A operator+(A lhs, const A& rhs) // note: lhs is passed by copy now
{
lhs += rhs;
return lhs;
}
A operator+(A lhs, int rhs) // note: lhs is passed by copy now
{
lhs += rhs;
return lhs;
}