Is operator lookup different for temporary/anonymous objects? - c++

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.

Related

C++ implicit constructor conversion followed by type upcast

I have a program that has a base class Value with multiple subclasses (e.g. IntValue) that inherit from Value. Each of these classes have constructors that accept one or more parameters. Here is example code showing what I'd like to be able to do:
#include <iostream>
class Value {
public:
Value() {}
virtual void print(std::ostream& os) const {}
};
class IntValue: public Value {
public:
IntValue(int val): val_(val) {}
void print(std::ostream& os) const override { os << val_; }
private:
int val_;
};
class VariableValue: public Value {
public:
VariableValue(const std::string& name): name_(name) {}
void print(std::ostream& os) const override { os << name_; }
private:
const std::string name_;
};
void emit_add(const Value& lhs, const Value& rhs, std::ostream& os) {
lhs.print(os);
os << " + ";
rhs.print(os);
os << std::endl;
}
template <class ValueType>
void emit_add(const ValueType& lhs, const ValueType& rhs, std::ostream &os) {
lhs.print(os);
os << " + ";
rhs.print(os);
os << std::endl;
}
int main() {
// all these work
emit_add<IntValue>(12, 13, std::cout); // implicit constructors
emit_add<VariableValue>(std::string("x"), std::string("y"), std::cout); // implicit constructo\
rs
emit_add(VariableValue(std::string("x")), IntValue(1), std::cout); // implicit upcasting
// this doesn't
emit_add(std::string("x"), 13, std::cout); // implicit constor + implicit upcasting
return -1;
}
When I try to compile using clang 9.1.0, I get the following error:
test.cpp:47:3: error: no matching function for call to 'emit_add'
emit_add(std::string("x"), 13, std::cout); // implicit constor + implicit upcasting
^~~~~~~~
test.cpp:25:6: note: candidate function not viable: no known conversion from 'std::string' (aka
'basic_string<char, char_traits<char>, allocator<char> >') to 'const Value' for 1st
argument
void emit_add(const Value& lhs, const Value& rhs, std::ostream& os) {
^
test.cpp:33:6: note: candidate template ignored: deduced conflicting types for parameter
'ValueType' ('std::__1::basic_string<char>' vs. 'int')
void emit_add(const ValueType& lhs, const ValueType& rhs, std::ostream &os) {
^
1 error generated.
My understanding is that the compiler is failing to call the implicit constructor for VariableValue AND THEN upcast it to type Value, but it can clearly do both individually.
Is it possible to force the compiler to do this?
A VariableValue is a Value (because inheritance is an "is a" relationship), but Value is not an VariableValue (the "is a" relationship of inheritance is one-way).
What I'm trying to say is that if you have a VariableValue object, you can easily go up the inheritance chain to get a Value object (or reference of it). But you can't go the other way, down the inheritance chain from a Value object, without being explicit about it.
You need to explicitly construct a VariableValue object and pass it to your function:
emit_add(VariableValue(x), 13, std::cout);
Consider the following as counter-example:
class Value
{
public:
Value() { }
Value(int) { }
Value(std::string const&) { }
};
emit_add(x, 13, std::cout);
This now would have worked as the compiler sees emit_add accepting two Values and Value having appropriate non-explicit constructors accepting std::string and int.
What C++ does not provide is inferring derived classes from base class according to given arguments, as Some programmer dude denoted already.
You could, though, provide a wrapper to do this work for you:
class Wrapper
{
std::unique_ptr<Value> value;
public:
Wrapper(int n) : value(new IntValue(n)) { }
Wrapper(std::string) : value(new VariableValue(n)) { }
friend std::ostream& operator<<(std::ostream& s, Wrapper const& w)
{
w.value->print(s);
return s;
}
};
If that's really better than specifying the types directly (as Some programmer dude did) is a matter of taste. On the other hand, as defined above (with operator<<), you now can do:
void emit_add(Wrapper const& lhs, Wrapper const& rhs, std::ostream& os)
{
os << lhs << " + " << rhs << std::endl;
}
which is a little more comfortable as well...
Another approach could be an overloaded template:
void emit_add(Value const& x, Value const& y, std::ostream& os);
template <typename TX, typename TY>
void emit_add(TX&& x, TY&& y, std::ostream& os)
{
emit_add
(
static_cast<Value const&>(TheValue<TX>(std::forward<TX>(x))),
static_cast<Value const&>(TheValue<TY>(std::forward<TY>(y))),
os
);
}
The casts above are necessary as otherwise, the template itself would be a better match and the non-template would not be selected, resulting in endless recursion. I converted the concrete values into a template for:
template <typename T>
class TheValue : public Value
{
public:
TheValue(T&& t)
: val_(std::forward<T>(t))
{ }
void print(std::ostream& os) const override
{
os << val_;
}
private:
T val_;
};
If this default pattern does not match your concrete needs for a specific type, you can specialise it for that type to suite your needs.
If you still need the original type names, you can alias them:
using IntValue = TheValue<int>;
Finally, if it is solely for printing out, you can do it directly, circumventing the Value class entirely:
template <typename TX, typename TY>
void emit_add(TX&& x, TY&& y, std::ostream& os)
{
std::cout << std::forward<TX>(x) << " + " << std::forward<TY>(y) << std::endl;
}
If you now have some custom types you want to print, just provide an operator<< overload for, such as for the following point example:
template <typename T>
class Point
{
T m_x, m_y;
public:
// constructors, calculations, ... (whatever you might need/find useful)
friend ostream& operator<<(ostream& s, Point const& p)
{
s << '(' p.m_x << ", " << p.m_y << ')';
}
};
Side note: The friend operator as above is convenient for your own data types (it still defines a free standing function), just if you did:
template <typename T>
ostream& operator<<(ostream& s, Point<T> const& p)
{
s << '(' p.x() << ", " << p.y() << ')';
}
outside the class (you still can declare it friend inside; if you don't or can't, you cannot use private members, of course, so you rely on the public interface as demonstrated above, assuming x and y are the public getters).

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);
}

Ambiguous use of operator<< inside my class

I have a problem with creating an operator<< in a class in this particular situation. I have a class that wraps a std::ostream so I can do some preprocessing for some types or some conditions before passing to the ostream, and want to pass some things straight through. I don't want to inherit the std::ostream, unless there is a good argument that I should. (I think I tried it once and found great difficulty and no success.)
I cannot use a template function because the processing depends on type in some cases, and I think the ambiguity would remain between it and those for my specific types (like 'Stuff'). Do I have to resort to using typeid ??
class MyClass
{
private:
std::ostream & m_out;
public:
MyClass (std::ostream & out)
: m_out(out)
{}
MyClass & operator<< (const Stuff & stuff)
{
//...
// something derived from processing stuff, unknown to stuff
m_out << something;
return *this;
}
// if I explicitly create operator<< for char, int, and double,
// such as shown for char and int below, I get a compile error:
// ambiguous overload for 'operator<<' on later attempt to use them.
MyClass & operator<< (char c)
{
m_out << c; // needs to be as a char
return *this;
}
MyClass & operator<< (int i)
{
if (/* some condition */)
i *= 3;
m_out << i; // needs to be as an integer
return *this;
}
// ...and other overloads that do not create an ambiguity issue...
// MyClass & operator<< (const std::string & str)
// MyClass & operator<< (const char * str)
};
void doSomething ()
{
MyClass proc(std::cout);
Stuff s1, s2;
unsigned i = 1;
proc << s1 << "using stuff and strings is fine" << s2;
proc << i; // compile error here: ambiguous overload for 'operator<<' in 'proc << i'
}
Your problem is that the value you're trying to insert is unsigned while the overloads you've provided only work on signed types. As far as the compiler is concerned, converting unsigned to int or char are both equally good/bad and result in ambiguity.
I cannot use a template function because the processing depends on type in some cases
Just make overloadings for those types.
I think the ambiguity would remain between it and those for my specific types (like 'Stuff').
No. If operator<< is overloaded for specific type, this overloading will be called. Otherwise will be called template function.
template <class T>
MyClass& operator<< (const T& t)
{
m_out << t;
return *this;
}

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.