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.)
Related
I have a large class, Base, that implements several different binary operations, some overloading the operators like *=, *, / etc, and some extra ones. The typical return type involves Base explicitly, for example:
class Base {
private:
std::vector<unsigned int> _arr;
public:
// These operations do something with _arr
Base& operator *= (const Base& rhs);
friend Base operator *(Base lhs, const Base& rhs);
std::vector<Base> myoperation(const Base& rhs);
};
I have a derived class, Derived, which simply adds extra structure, so it looks like this:
class Derived : public Base {
private:
std::vector<int> _arr_derived;
public:
// This operation does something with _arr and _arr_derived;
Derived& my_derived_operation(const Derived& rhs);
// These operations are exactly the same implementation as the one in Base!
Derived& operator *= (const Derived& rhs) {
Base::operator*=(rhs);
return *this;
}
friend Derived operator *(Derived lhs, const Derived& rhs) {
lhs *= rhs;
return lhs;
}
std::vector<Derived> myoperation(const Derived& rhs) // <---- what to do here?
};
What do I need to do with the operation myoperation? That returns a vector. There is no extra logic on all operations that are inherited, the only difference is in the return type. Is there any way to avoid the code duplication? Even the declaration of the operations is bothersome.
I thought perhaps defining the base operations as template functions returning the same type as the argument, and requiring the template parameter to be a derived class:
class Base {
// on C++20
template<class C> requires std::is_base_of_v<Base,C>
C& operator*=(const C& rhs);
}
Does this work? Even if it does, is there a better mechanism?
Edit to reply to Remy Lebeau's comment: I want to emphasize that all operations that appear both in Derived and in Base are meant to be the same, there is not extra logic except the returning type. The example of operator *= already shows this, just calling the Base version. A possible implementation, albeit stupid, of what I mean by myoperation could be this:
std::vector<Base> Base::myoperation(const Base& rhs) {
std::vector<Base> ret{};
ret.push_back(rhs*rhs);
return ret;
}
std::vector<Derived> Derived::myoperation(const Derived& rhs) {
std::vector<Derived> ret{}
ret.push_back(rhs*rhs);
return ret;
}
With the templated version I could write only
class Base {
teplate<class C> requires std::is_base_of_v<Base,C>
auto myoperation(const C& rhs) {
std::vector<C> ret;
ret.push_back(rhs*rhs);
return ret;
}
};
And that's what I meant by the templated solution above, so that I do not need to even declare myoperation on Derived.
I went with the templated version, although using pointers is standard in polymorphism, I have too many vector valued operations and are complicated enough to change the logic, and also wouldn't want to have to deal with recasting to Derived classes when necessary. Templating also has the advantage that no code needs to be written on the derived classes. So on base all operations that transform the object in place are left as they were and the ones that produce new ones are templated. I appreciate if I could be pointed to pitfalls of this approach
#include <iostream>
#include <vector>
class Base {
int _a;
public:
Base(int a) : _a{a} {};
Base& operator *= (const Base& rhs) {
_a *= rhs.a();
return *this;
}
template <class C> requires std::is_base_of_v<Base,C>
friend C operator *(C lhs, const Base& rhs) {
lhs *= rhs;
return lhs;
}
template <class C> requires std::is_base_of_v<Base,C>
std::vector<C> myop (const C& rhs) {
std::vector<C> ret {};
ret.push_back(rhs*rhs);
return ret;
}
int a() const { return _a; }
};
class Derived : public Base {
int _b;
public:
Derived(int a, int b) : Base{a}, _b {b} {}
int b() const {return _b;}
};
int main() {
Derived C{9,5};
Derived D{4,7};
auto E = C*D;
auto F = D.myop(C);
std::cout << E.a() << ", " << E.b() << std::endl;
std::cout << F.front().a() << ", " << F.front().b() << std::endl;
return 0;
}
OUTPUT:
36, 5
81, 5
As title, can be to overloading operator = for casting?
I have a simple class.
class A{
protected:
int m_int;
public:
A& operator=( int& obj)
{
m_int = obj;
return *this;
}
};
I want:
A t_a = 1;
and
int t_int = t_a;
Is there a way to do this?
Just define conversion operator
operator int() const
{
return m_int;
}
or
explicit operator int() const
{
return m_int;
}
In the last case you have to use an explicit casting in the statement
int t_int = int( t_a );
Take into account that the assignment operator should be declared like
A& operator=( const int& obj)
{
m_int = obj;
return *this;
}
or like
A& operator=( int obj)
{
m_int = obj;
return *this;
}
Otherwise it will be impossible to bind the non-constant reference with integer literals or temporary values.
As for the assignment operator then you may define only a compound assignment operator for the type int and the type A.
For example you could define the operator += or something other operator.
Yes, that’s possible. You need a custom ctor and assignment operator. But writing those disables some of the compiler generated ctors/assignment ops. If you still need/want them, you need to reintroduce them explicitly.
class A
{
protected:
// Note the initializer. Without it m_int is uninitialized
// when you default construct an A. That’s a common source
// of bugs.
int m_int = 0;
public:
// Following two are the important ones
A(int i) : m_int(i) {}
A& operator=(int i)
{
m_int = i;
return *this;
}
// if A needs to be default constructible as well
A() = default;
// The int ctor/assignment disable the compiler generated
// normal copy ctor/assignment (the ones that take another A).
// Reintroduce them like this:
A(const A&) = default;
A& operator=(const A&) = default;
// Writing any copy ctor/assignment disables the compiler generated
// move ctor/assignment. If you still want them, reintroduce them.
A(A&&) = default;
A& operator=(A&&) = default;
};
A t_a = 1;
This doesn't use assignment. You need a constructor which takes an int argument.
int t_int = t_a;
You will need operator int() for this.
Note that it is a really bad idea to have a class which has both an implicit constructor from a type, and an implicit cast to the type. You will get all sorts of confusing errors when you try to do overload resolution.
Instead, I would make the constructor explicit, and write an explicit conversion function. That means you have to write:
int t_int = t_a.to_int();
But at least it's explicit.
Edit: Note that you can overload operator = for casting (either inside or outside the class), but neither of the code samples you gave will use it. = is used both for assignment and initialization, and both your samples are initialization (so won't use operator =)
I don't understand what is going on in the following piece of code:
struct A { };
struct B {
B() { }
B(const A&) { }
friend B operator*(const B&, const B&)
{
return B();
}
};
int main()
{
B x = A() * A();
return 0;
}
When I compile (with both clang and gcc 4.9.2) I get an error message on the "B x = A() * A()" line; clang says "invalid operands to binary expression".
If I take the operator* definition from inside the class, everything is 100% ok!
struct A { };
struct B {
B() { }
B(const A&) { }
friend B operator*(const B&, const B&);
};
B operator*(const B&, const B&)
{
return B();
}
int main()
{
B x = A() * A();
return 0;
}
What is going on?
Since operator*() is defined inside the function as a friend, it can only be found by ADL, not normal unqualified lookup. This type of lookup requires that the arguments be exact types, not types that are implicitly convertible. This means the operator cannot be found even if A can be converted to B.
When you declare the function outside the class, then it can be found by the normal unqualified lookup rules.
I have a template class, looking something like:
template<class T>
class A
{
public:
operator T() const { return value;}
operator T&() { return value;}
private:
T value;
}
It seems that the operatorT() const is never called. Even in a statement like this
const int a = myA;
where myA is a instance of A.
Is there something wrong, missing with the above code?
The operator will take effect only when you define an object of type const A<T>. For example:
const A<int> myA;
int someInt = myA;
will call operator T() const.
And, since it's of course true - like Benjamin Lindley pointed out - if you access the object through a const reference.
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;
}