Unintended behavior from boost::operators - c++

I'm taking boost::operators (clang 2.1, boost 1.48.0) for a spin, and ran into the following behavior I can't explain. It seems that when I add my own operator double() const method to my class Ex (as I'd like to allow my users to idiomatically use static_cast<double>() on instances of my class), I no longer get a compiler error when trying to use operator== between dissimilar classes. In fact, it seems that operator== is not called at all.
Without operator double() const, the class works completely as expected (save for that it now lacks a conversion operator), and I receive the correct complier error when trying f == h.
So what is the right way to add this conversion operator? Code below.
// clang++ -std=c++0x boost-operators-example.cpp -Wall -o ex
#include <boost/operators.hpp>
#include <iostream>
template <typename T, int N>
class Ex : boost::operators<Ex<T,N>> {
public:
Ex(T data) : data_(data) {};
Ex& operator=(const Ex& rhs) {
data_ = rhs.data_;
return *this;
};
T get() {
return data_ * N;
};
// the troubling operator double()
operator double() const {
return double(data_) / N;
};
bool operator<(const Ex& rhs) const {
return data_ < rhs.data_;
};
bool operator==(const Ex& rhs) const {
return data_ == rhs.data_;
};
private:
T data_;
};
int main(int argc, char **argv) {
Ex<int,4> f(1);
Ex<int,4> g(2);
Ex<int,2> h(1);
// this will fail for obvious reasons when operator double() is not defined
//
// error: cannot convert 'Ex<int, 4>' to 'double' without a conversion operator
std::cout << static_cast<double>(f) << '\n';
std::cout
// ok
<< (f == g)
// this is the error I'm supposed to get, but does not occur when I have
// operator double() defined
//
// error: invalid operands to binary expression
// ('Ex<int, 4>' and 'Ex<int, 2>')
// note: candidate function not viable: no known conversion from
// 'Ex<int, 2>' to 'const Ex<int, 4>' for 1st argument
// bool operator==(const Ex& rhs) const
<< (f == h)
<< '\n';
}

You should mark your operator double() as explicit. That allows the static cast, but prevents it being used as an implicit conversion when you test for equality (and in other cases).

Related

Is operator lookup different for temporary/anonymous objects?

I am trying to understand why named and temporary (anonymous?) objects seem to behave differently when looking up an operator defined in a base class. In the following code I've made a wrapper around a variable of 'mytype' which I want to switch between double and std::complex through a compiler definition. To this wrapper I want to add an operator using a base type (similar to boost::operators). My interest is in the difference between the two (admittedly quite forced) cout lines in 'main'.
#include <iostream>
#include <cmath>
#include <complex>
#ifdef COMPLEX
typedef std::complex<double> mytype;
#else
typedef double mytype;
#endif
template <typename DerivedType, typename integertype>
struct Base
{
friend DerivedType& operator +=(DerivedType& lhs, const integertype& rhs)
{
std::cout << "base += version" << std::endl;
return lhs += mytype(rhs);
}
};
struct Wrapper : public Base<Wrapper, unsigned>
{
Wrapper(const mytype& rhs) : m_value(rhs) {}
Wrapper(const unsigned& rhs) : Wrapper(mytype(rhs)) {}
Wrapper& operator += (const Wrapper& rhs)
{
std::cout << "wrapper version" << std::endl;
m_value += rhs.m_value;
return *this;
}
Wrapper& operator += (const mytype& rhs)
{
std::cout << "wrapper mytype version" << std::endl;
m_value += rhs;
return *this;
}
mytype m_value;
};
int main()
{
std::cout << (Wrapper(2.0) += 3u).m_value << std::endl;
Wrapper t_MyWrapper(2.0);
std::cout << (t_MyWrapper += 3u).m_value << std::endl;
}
if I compile without -DCOMPLEX I get the following output:
wrapper mytype version
5
base += version
wrapper mytype version
5
As far as I can tell, the first output in main ignores the operator+=(Wrapper&, const unsigned&) from the Base. It instead promotes the unsigned to a double (which is preferential to converting to a Wrapper) and calls the operator+=(Wrapper& const double&). The 'named object' however, does call the operator from the base type. Compiling with -DCOMPLEX leads to a compile error:
SimplifiedProblem.cxx: In function ‘int main()’:
SimplifiedProblem.cxx:47:32: error: ambiguous overload for ‘operator+=’ (operand types are ‘Wrapper’ and ‘unsigned int’)
47 | std::cout << (Wrapper(2.0) += 3u).m_value << std::endl;
| ~~~~~~~~~~~~~^~~~~
SimplifiedProblem.cxx:28:14: note: candidate: ‘Wrapper& Wrapper::operator+=(const Wrapper&)’
28 | Wrapper& operator += (const Wrapper& rhs)
| ^~~~~~~~
SimplifiedProblem.cxx:35:14: note: candidate: ‘Wrapper& Wrapper::operator+=(const mytype&)’
35 | Wrapper& operator += (const mytype& rhs)
| ^~~~~~~~
Considering that converting the unsigned to std::complex is not 'better' than to a 'Wrapper' (neither is a scalar type), the compile error makes sense, but I would like to understand why the operator from the base type is not used.
Moving the operator+=(Wrapper&, const unsigned&) to the wrapper directly avoids this problem, and removing the constructor Wrapper(const unsigned&) resolves the ambiguity when -DCOMPLEX. But I would like to understand if the rules for temporary object lookup are different or if something else is causing this behaviour.
The 3u literal converts implicitly into a Wrapper and thus both overloads of operator+= are possible candidates in the line the compiler flags to you.
One simple solution is to mark both Wrapper constructors with explicit. Then, they will never allow implicit conversion to a Wrapper but will always require you to type out the conversion as you did in the first line of main.

Why do conversion operators cause ambiguous overload when const-ref and value exist

I'm looking at a wrapping class, based on https://www.fluentcpp.com/category/strong-types/ The main difference is that I'm replacing the get() method with a explicit casting operator as this triggers questions during code review when used.
As you can see in the simplified code below, I have 3 overloads of the casting operator:
From const A & to int
From A && to int
From const A & to const int &
When writing: static_cast<int>(a), I expect the overload of const A & to int to be used. However, it seems to favor the int and the const int & overload equally. Why does it do so?
Similarly to this, it seems to allow const int &r = static_cast<const int &>(createA()); which I assume is a life-time bug. (assuming createA returns an A by value)
Simplified code at Compiler Explorer: https://gcc.godbolt.org/z/YMH9Ed
#include <utility>
struct A
{
int v = 42;
explicit operator int() const & { return v; } // Removing this line works
explicit operator int() && { return std::move(v); }
explicit operator const int &() const & { return v; }
};
int main(int, char**)
{
A a;
int r = static_cast<int>(a);
return r;
}
Compilation error:
<source>:14:13: error: ambiguous conversion for static_cast from 'A' to 'int'
int r = static_cast<int>(a);
^~~~~~~~~~~~~~~~~~~
<source>:6:14: note: candidate function
explicit operator int() const & { return v; }
^
<source>:8:14: note: candidate function
explicit operator const int &() const & { return v; }
^
explicit operator int() const & { return v; }
and
explicit operator const int &() const & { return v; }
are equally good conversions. a is a lvalue so both functions can be called. a is also not const so both functions will have to apply a const conversion to a, so they are both still equally good. All that is left is the "return type", int or const int&, but those are both equally good to create anint from.
You need to get rid of one of conversion operators, or remove the constness
from
explicit operator const int &() const & { return v; }
to turn it into
explicit operator const int &() & { return v; }
so non const lvalues give you a const reference.

Understanding C++ choice of conversion operator

I've got an optional-like class (I can't use optional since it's in C++17). It contains a (possible) value along with a flag indicating if it's valid. I've got an explicit bool operator and a conversion operator to get the value out. The problem is, sometimes C++ will choose the bool operator in an explicitly bool context (an if statement), and other times it won't. Can anyone help me understand this behavior:
#include <algorithm>
#include <stdexcept>
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
operator T&&() {
return std::move(value());
}
explicit operator bool() const {
return valid_;
}
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
int main() {
// fine, uses operator bool()
maybe<std::pair<int,int>> m0;
if (m0) {
std::pair<int,int> p = m0;
(void)p;
}
// throws error, uses operator T&&()
maybe<double> m1;
if (m1) {
double p = m1;
(void)p;
}
}
Whenever you write:
if (x)
That is equivalent to having written:
bool __flag(x);
if (__flag)
This is called a contextual conversion to bool (note that it's direct-initialization, so the explicit conversion function is a candidate).
When we do overload resolution on that construction for m0, there's only one valid candidate: explicit operator bool() const.
But when we do overload resolution on that construction for m1, there are two: explicit operator bool() const and operator double&&(), because double is convertible to bool. The latter is a better match because of the extra const qualification on the bool conversion function, even though we have to do an extra double conversion. Hence, it wins.
Would simply remove operator T&&() from your interface, as it doesn't make much sense for this type.
As soon as T is convertible to bool (double is, std::pair is not) your two operators will match and you'll get an ambiguous call, or one may be a better match for some reason.
You should only provide one of the two operators, not both.
Your operator bool and conversion operator are ambiguous in this design.
In the first context, std::pair<int,int> does not cast to bool
so the explicit bool conversion operator is used.
In the second context, double does cast to bool so the T
conversion operator is used, which returns a double, which then
casts to bool implicitly.
Note in the second context, you are calling std::move which puts value in a valid but undefined state, which leads to undefined behavior when you cast value to double a second time in the if block.
I'd use a named member function to indicate if it is valid, and modify the conversion operator:
#include <algorithm>
#include <stdexcept>
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
operator T&&() && { return std::move(value_); } // if rvalue
operator T&() & { return value_; } // if lvalue
operator const T&() const & { return value_; } // if const lvalue
bool valid() const { return valid_; }
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
int main() {
// fine, uses operator bool()
maybe<std::pair<int,int>> m0;
if (m0.valid()) {
std::pair<int,int> p = m0;
(void)p;
}
// throws error, uses operator T&&()
maybe<double> m1;
if (m1.valid()) {
double p = m1;
(void)p;
}
}
EDIT: The conversion operator should only move from member value_ if the maybe object is an rvalue reference. Using && after a functions signature specializes for this case -- please see Kerrek SB's answer for more information.

Typecast overloading

I could overload '+' operator but I am not sure how I could typecast for the NewType. I would like it to be typecasted to any other variable type. Can you please provide some pointers? Thanks a lot!
#include <iostream>
class NewType {
private:
float val;
public:
NewType(float v) { val = v; }
friend NewType operator+(const NewType &c1, const NewType &c2);
float GetVal() const { return val; }
};
NewType operator+(const NewType &c1, const NewType &c2) { return NewType(c1.val + c2.val); }
int main() {
NewType a = 13.7;
// Here is the problem, I would like this to print 13.
std::cout << (int) a << std::endl;
return 0;
}
I would like it to be typecasted to any other variable type.
For any other variable type you need a templated user-defined conversion:
class NewType {
public
// ...
template<typename T>
explicit operator T() const { return T(val); }
// ...
};
explicit (here C++11 and up) makes sure you will use explicit cast, i.e.:
NewType a = 13.7;
int n = a; // compile error
int n2 = static_cast<int>(a); // now OK
You could also use uniform initialization in your user-defined conversion operator:
template<typename T>
explicit operator T() const { return T{val}; }
this will give you additional warning in case your cast could require narrowing. But as I see under gcc this generates only warnings by default (as I remember this is by design - due to lots of legacy code would break), under clang it generates error:
main.cpp:15:16: error: type 'float' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
return T{val};
and the same Visual Studio generates error.

C++ Trick to avoid pointer comparison

I am moving a code base from one programming style to another.
We have a type called Operand defined like:
class Operand
{...};
Then we had
class OperandFactory
{
public:
const Operand *make_operand (...);
};
OperandFactory used to hash the Operand and keep it in a table. Therefore if you called make_operand with the same arguments, you would get the same pointer and pointer comparison over Operands proliferated. Now I need to add a feature that will make this infeasible. So, I implement operator== in Operand and would like to somehow generate at compile time (better) or run time (better than nothing) error if I ever do a pointer comparison on Operands. What's the best way to achieve this?
This is only to be used during this transition stage, so I don't mind if the solution looks like a hack as long as it captures all comparisons in the code base.
you can overload the address of operator to return a handle and declare the comparison of two handles (without definition). This would lead to a linker error.
#include <iostream>
class Op;
class Handle {
Op *pri_;
public:
explicit Handle(Op *o) : pri_(o) {}
Op *operator->() const { return pri_; }
Op &operator*() const { return *pri_; }
};
// force compile time errors on comparison operators
bool operator==(const Handle &, const Handle &) = delete;
bool operator!=(const Handle &, const Handle &) = delete;
bool operator>=(const Handle &, const Handle &) = delete;
bool operator<=(const Handle &, const Handle &) = delete;
bool operator<(const Handle &, const Handle &) = delete;
bool operator>(const Handle &, const Handle &) = delete;
class Op {
int foo_;
public:
explicit Op(int i) : foo_(i) { }
Handle operator&() { return Handle(this); };
void touch() const { std::cout << "foobar"; }
};
int main(int argc, char **argv) {
Op i{10};
Op j{20};
auto c = &j; // works
c->touch(); // works
(*c).touch(); // works
if (&j == &i) {
/* will not compile */
}
}
Note:
You have to fulfill the random_access_iterator requirement for Handle!
Op i{10}
Handle ref = &i;
ref++; ref--; ++ref; --ref; ref = ref + 10; ref = ref - 10; // should all work.
Adding an operator in your Operand class won't help : you want to detect comparisons of pointers to Operands. Unfortunately, native types operators can't be overloaded, and pointers are of native type. This is not the solution you're looking for.