Why doesn't explicit bool() conversion happen in contextual conversion - c++

If the following test-programm
#include <iostream>
class A {
public:
A() {}
explicit operator bool() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
return true;
}
// explicit operator bool() {
// std::cout << __PRETTY_FUNCTION__ << std::endl;
// return true;
// }
const operator int() const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
return 1;
}
operator int() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
return 1;
}
};
int main() {
A a;
if (a) {
std::cout << "bool()" << std::endl;
}
if (a + 0) {
std::cout << "int()" << std::endl;
}
}
is run, the output is
int A::operator int()
bool()
int A::operator int()
int()
and not
bool A::operator _Bool()
bool()
int A::operator int()
int()
what I expected (and what you get if you uncomment the commented parts).
So the question is what are the rules giving the conversion to non-const-int precedence over converting to const-bool?

When performing overload resolution on a reference binding, the less cv-qualified type is preferred. This is discussed in 13.3.3.2p3, with the example given:
struct X {
void f() const;
void f();
};
void g(const X& a, X b) {
a.f(); // calls X::f() const
b.f(); // calls X::f()
}
Note that binding an object to the implicit object parameter of a member function (13.3.1.1.1p2) is a reference binding (13.3.3.1.4).
Conversion operators are treated as member functions (13.3.1.5) for the purposes of overload resolution (13.3p2). Contextual conversion to bool has the semantics of initialization (4p4).
Importantly, any conversion required on the return type of the conversion operator is considered only after considering overload resolution between the conversion operators themselves (13.3.3p1).
The solution is to ensure that all conversion operators have the same const-qualification, especially to scalar type.

what are the rules giving the conversion to non-const-int precedence over converting to const-bool?
Using const member function of a non-const object, requires conversion of a non-const object into const-object. That is why operator int() has a better match over operator bool() const.
To make it a bit clearer, if you would remove int operators, what really happens with the first bool context (first if) is this :
if ( const_cast<const A&>(a).operator bool() ) {
Instead, what happens is this :
if ( a.operator int() )

Related

C++ type conversion operator

I am studying operator overloading, there are some parts that are difficult to understand.
See this example code.
class A {
private:
char a;
int b;
double c;
public:
A(char _a = 'a', int _b = 99, double _c = 1.618) :a(_a), b(_b), c(_c){
}
public:
operator char() const {
cout << "operator char() called" << endl;
return this->a;
}
operator int() const {
cout << "operator int() called" << endl;
return this->b;
}
operator double() {
cout << "operator double() called" << endl;
return this->c;
}
};
int main(void) {
A a;
char b = a;
int c = a;
double d = a;
printf("%c\n", b);
printf("%d\n", c);
printf("%f\n", d);
return 0;
}
I made this code to test for type conversion operator and expected that the appropriate function would be called for each type of data.
But the result is..
operator double() called
operator double() called
operator double() called
<-- strange character is gone on board!
1
1.618000
I can not understand why the results are not as follows.
operator char() called
operator int() called
operator double() called
a
99
1.618
Why is double operator called when converting to char and int?
Have a good day! :)
You forgot the const on the double conversion operator:
operator double() const { // <---------------------------
cout << "operator double() called" << endl;
return this->c;
}
};
As in your example a is not const, the double conversion is the best match. If you fix that you get the expected output.
Live example
...some opinion based PS:
I didnt find what the core guidelines say about conversion operators, but if I had to make up a guideline for conversion operators it would be: Avoid them. If you use them, make them explicit. The surprising effects of implicit conversion outweigh the benefits by far.
Just as an example, consider std::bitset. Instead of offering conversion operators it has to_string, to_ulong and to_ullong. It is better to have your code explicit. A a; double d = a; is a little bit mysterious. I would have to look at the class definition to get an idea of what is really going on. On the other hand A a; double d = a.as_double(); can do the exact same thing, but is way more expressive.
Yea so the problem is, that you made all operators const except for the double operator. I am still a bit surprised because this const just means that the operator call does not modify the class members. Still it seems that only the double operator is called for all 3. I would all 3 op's make const and then it will work properly.
If someone has an explanation why this happens, I would also like to know.
Cheers.
operator char() const { // here is const
cout << "operator char() called" << endl;
return this->a;
}
operator int() const { // here is const
cout << "operator int() called" << endl;
return this->b;
}
operator double() { // here is no const
cout << "operator double() called" << endl;
return this->c;
}

Error when using operator << with implicitly converted non-fundamental data types [duplicate]

This question already has answers here:
Overload resolution failure when streaming object via implicit conversion to string
(5 answers)
Closed 4 years ago.
I have a struct that works as a wrapper for other types as follows:
template<typename T>
struct A {
A& operator=(const T& value){
m_value = value;
return *this;
}
operator T() const {
return m_value;
}
private:
T m_value;
};
I use it like this:
int main() {
A<int> a;
a = 5; // Copy assignment constructor
std::cout << a << "\n"; // Implicit conversion to int
}
which works as expected. My problem occurs when using non-fundamental types as the following example shows:
int main() {
A<std::complex<int>> c;
c = std::complex<int>(2, 2);
std::cout << c << "\n";
}
The snippet above raises an invalid operands to binary expression error.
Why does this error occur? Why isn't the overloaded operator << of std::complex<int> used with the implicitly converted A<std::complex<int>>?
The stream operators for std::complex are template functions. They will not be invoked unless you actual have a std::complex as no conversions happens in template argument deduction. This means the compiler will not find a suitable overload to print a A<std::complex<int>>
You're first case for works because std::basic_ostream::operator << is overloaded to take an int and you are allowed one user defined conversion is overload resolution.
As a work arround you can define your own operator << that takes your wrapper and forward to the underlying types' operator <<. That would look like
template<typename T>
std::ostream& operator <<(std::ostream& os, const A<T>& a)
{
return os << static_cast<T>(a);
}

Weird operator overloading, "operator T& () const noexcept { return *_ptr; }"

I studied that the format of operator functions is
(return value)operator[space]op(arguments){implementation}
But, In std::reference_wrapper implementation, there is an operator overloading function declared as operator T& () const noexcept { return *_ptr; }.
Is this operator different from T& operator () const noexcept { return *_ptr; } ?. If both are different, then what is the use of the first?
operator T& () const noexcept; is a user-defined conversion function. std::reference_wrapper has it in order to give you access to the stored reference, without changing the syntax:
int x = 42;
int &a = x;
std::reference_wrapper<int> b = x;
std::cout << a << " " << b << std::endl;
Assignment is a little bit trickier.
T& operator () const noexcept; is an attempt to declare operator(), but fails to compile due to missing parameter list. The right syntax would be:
T& operator ()( ) const noexcept;
// ^
// parameters here
and the usage is completely different.

Unintended behavior from boost::operators

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).

Why doesn't my overloaded comma operator get called?

I'm trying to overload the comma operator with a non-friend non-member function like this:
#include <iostream>
using std::cout;
using std::endl;
class comma_op
{
int val;
public:
void operator,(const float &rhs)
{
cout << this->val << ", " << rhs << endl;
}
};
void operator,(const float &lhs, const comma_op &rhs)
{
cout << "Reached!\n"; // this gets printed though
rhs, lhs; // reversing this leads to a infinite recursion ;)
}
int main()
{
comma_op obj;
12.5f, obj;
return 0;
}
Basically, I'm trying to get the comma operator usable from both sides, with a float. Having a member function only allows me to write obj, float_val, while having an additional helper non-friend non-member function allows me to write float_val, obj; but the member operator function doesn't get called.
GCC cries:
comma.cpp: In function ‘void operator,(const float&, const comma_op&)’:
comma.cpp:19: warning: left-hand operand of comma has no effect
comma.cpp:19: warning: right-hand operand of comma has no effect
Note:
I realise that overloading operators, that too to overload comma op., Is confusing and is not at all advisable from a purist's viewpoint. I'm just learning C++ nuances here.
void operator,(const float &rhs)
You need a const here.
void operator,(const float &rhs) const {
cout << this->val << ", " << rhs << endl;
}
The reason is because
rhs, lhs
will call
rhs.operator,(lhs)
Since rhs is a const comma_op&, the method must be a const method. But you only provide a non-const operator,, so the default definition will be used.