Trouble combining templates and class function overloading - c++

The code below works fine, printing out 50 as expected. What I am not understanding, however, is why the same program can't be written with a couple slight alterations to this code. The two lines of proposed code are marked Bad code 1 and 2. When these are substituted for the current working code to the left of them (i.e. in addStuff and main), I get the following error:
error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream}' and 'MyClass')|
The way I expected it to work was: when addStuff(x,y) gets called in the (bad) cout line of main(), it first evaluates x+y, the behavior of which is defined by the operator+ MyClass overloaded member function. This should just return a MyClass object, which then has no trouble calling getVar.
What am I missing here?
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass(){}
MyClass(int a) : priV(a){}
MyClass operator+(MyClass otherMC)
{
MyClass newMC;
newMC.priV = this->priV + otherMC.priV;
return newMC;
}
int getVar(){return priV;}
void setVar(int v){priV=v;}
private:
int priV;
};
template <class gen>
gen addStuff(gen x, gen y)
{
return x+y; //Bad code 1: return (x+y).getVar();
}
int main()
{
MyClass x(20), y(30);
cout << addStuff(x,y).getVar() << endl; //Bad code 2: cout << addStuff(x,y) << endl;
}

Your interpretation of what should happen in line 1, return (x+y).getVar(); is correct. The overloaded operator+ will get called on the arguments x and y, and getVar() will be called on the result of operator+. When x and y are of type MyClass, the operator+ call returns an object of type MyClass, so the call to getVar() works.
However, getVar() returns an int. Your function addStuff is specified to return gen, which is the same template parameter that matches the arguments x and y. This means that when x and y are MyClass, the addStuff function must return MyClass.
Thus, when you put addStuff(x,y) in your cout statement, the return type of addStuff is inferred to be MyClass. This is what makes your compiler produce the error that "there is no operator<< for type MyClass."
If you want addStuff to return the result of getVar(), you should declare it to return int, not gen.

Problem is in addStuff function, it returns MyClass object, constructed from int variable. You need to modify this function to work with "bad code"
template <class gen>
int addStuff(gen x, gen y)
{
return (x+y).getVar();
}
Or write an ostream operator for MyClass. For this you need to modify getVar method, include a friend declaration and implement it
int getVar() const { return priV; }
friend std::ostream& operator<< (std::ostream &out, const MyClass& m);
std::ostream& operator<< (std::ostream& out, const MyClass& m) {
out << m.getVar();
return out;
}
This way you don't need to modify addStuff function.
Side note, your code didn't compile for me because there was no default constructor in MyClass, had to modify constructor like this
MyClass(int a = 0) : priV(a) {}

You have to modify addStuff so that it returns an int, not the template parameter gen (which will be MyClass):
template <class gen>
int addStuff (gen x, gen y)
{
return (x + y).getVar ();
}
If you don't change gen into int, then the function will work fine because the constructor MyClass(int a) will be called explicitly and then the result will be an object of type MyClass.
And obviously the compiler will say that you cannot 'cout' an object of type MyClass.
So i suggest you to mark the constructor with explicit:
Also your code didn't compile because there is no default constructor, so add one, or simply mark the parameter with a default value:
explicit MyClass (int a = 0) : priV (a) {}
EDIT:
If you don't want to change the return type of the function if you change the type of the member, you can use decltype, to make the code more generic:
template <class gen>
decltype (auto) addStuff (gen x, gen y) //Or decltype (declval<gen> ().getVar ())
{
return (x + y).getVar ();
}

Related

Question about overloaded operator T() in C++ template class

I have this little piece of code:
template<typename T>
class Test
{
public:
//operator T() const { return toto; }
T toto{ nullptr };
};
void function(int* a) {}
int main(int argc, char** argv)
{
Test<int*> a;
function(a);
return 0;
}
It doesn't compile unless the line operator T() const { return toto; } is un-commented. This magically works, but I am not sure why (if I un-comment the line).
I do understand that if the line is commented, the type of a when passed to function() is incompatible with the expected type int*. So, of course, the compiler complains ... no problem.
I also understand that the operator returns the actual type of the object, therefore in this particular case the compiler is happy.
I don't understand why the operator is called in this particular case.
Is doing function(a) the same thing as doing function(a()), only the () are implicit?
operator T() const { return toto; } is a user defined conversion operator, it is not operator(). It's used to define that your class is convertible to a different type.
operator() would look like this instead:
void operator()() const { ... }
In your case, you are using int* as T. If you substitute it yourself in the operator, you will see that it becomes operator int*() const { return toto; } which means "my class can be converted to an int* and the result of that conversion is evaluated as return toto;".
The function function() only accepts an int* as its argument. When you provide a Test instance, the call is only legal if there is a way to convert from Test to int*, which is why the operator T is required for the code to compile.

C++ member functions with same name and parameters, different return type

is it valid if I define member functions with same name&parameters but different return types inside a class like this:
class Test {
public:
int a;
double b;
}
class Foo {
private:
Test t;
public:
inline Test &getTest() {
return t;
}
inline const Test &getTest() const {
return t;
}
}
Which member function gets called if we have following code?
Foo foo; // suppose foo has been initialized
Test& t1 = foo.getTest();
const Test& t2 = foo.getTest();
no, it is not valid, but in your example it is, because the last const is actually part of the signature (the hidden Foo *this first parameter is now const Foo *this).
It is used to access in read-only (get const reference, the method is constant), or write (get non-const reference, the method is not constant)
it's still a good design choice to return the reference of the same entity (constant or non-constant) in both methods of course!
No.
You cannot overload on return type.
Why? The standard says so.
And it actually makes sense - you can't determine what function to call in all situations.
Const and non-const methods with the same formal parameter list can appear side-by-side because the this pointer is treated as a hidden argument and would have a different type. This may be used to provide mutating and non-mutating accessors as in the code in your question.
If the signatures are exactly the same, then no.
To expand upon the previous answers and your given code with an example so you can actually tell what's being called when:
#include <iostream>
class Test {
public:
int a;
double b;
};
class Foo {
private:
Test t;
public:
inline Test &getTest() {
std::cout << "Non const-refrence " << std::endl;
return t;
}
inline const Test &getTest() const {
std::cout << "Const-refrence " << std::endl;
return t;
}
};
int main() {
Foo foo;
Test& t1 = foo.getTest();
const Test& t2 = foo.getTest();
const Foo bar;
const Test& t3 = bar.getTest();
return 0;
}
With output:
Non const-refrence
Non const-refrence
Const-refrence
The const you see after the second getTest signature tells the compiler that no member variables will be modified as a result of calling this function.
is it valid if I define member functions with same name&parameters but different return types [...]?
No.
Neither a method class nor a non-class function.
The reason is ambiguity. There would be situation in which the compiler could not pick the right overloading only by deducing the returned value.
In conclusion: you can't overload methods based on return type.
In your example, those two methods:
Test& getTest();
const Test& getTest() const;
Are correctly overloaded because the signature is different, but not because the return value is different!
Indeed, a function signature is made up of:
function name
cv-qualifiers
parameter types
method qualifier
So the signature of your methods are:
1) getTest();
2) getTest() const;
^------ Note that const qualifiers of the method is part of signature
As you can notice, the return value is not part of signature, but the const of the method qualifier is.
Which member function gets called if we have following code?
With the following code:
Foo foo;
Test& t1 = foo.getTest();
const Test& t2 = foo.getTest();
It will call only the no-const method, even in the case t2.
The reason is that foo object is no-const in that scope, so each
method will be called in its no-const form.
In details, in the third line:
const Test& t2 = foo.getTest();
foo.getTest() will return the no-const reference and after will
be implicitly converted in a const reference.
If you want to force the compiler to call the const version, you should "temporary convert" the object foo in a const.
For example:
const int& t2 = static_cast<const Foo&>(foo).getTest();
In that case I get a const ref to the object, so the object will be treated like a const and the proper const method will be invoked.

Overloading Based on L-Value versus R-Value

I found in a C++ book the following:
Although we will not be doing it in this book, you can overload a
function name (or operator) so that it behaves differently when used
as an l-value and when it is used as an r-value. (Recall that an
l-value means it can be used on the left-hand side of an assignment
statement.) For example, if you want a function f to behave
differently depending on whether it is used as an l-value or an
r-value, you can do so as follows:
class SomeClass {
public:
int& f(); // will be used in any l-value invocation const
const int& f( ) const; // used in any r-value invocation ...
};
I tried this and it didn't work:
class Foo {
public:
int& id(int& a);
const int& id(int& a) const;
};
int main() {
int a;
Foo f;
f.id(a) = 2;
a = f.id(a);
cout << f.id(a) << endl;
}
int& Foo :: id(int& a) {
cout << "Bar\n";
return a;
}
const int& Foo :: id(int& a) const {
cout << "No bar !\n";
return a;
}
Have I wrongly understood it ?
Either the book's example is flat-out wrong, or you copied the wrong example from the book.
class SomeClass {
public:
int& f(); // will be used in any l-value invocation const
const int& f( ) const; // used in any r-value invocation ...
};
With this code, when you call s.f() where s is an object of type SomeClass, the first version will be called when s is non-const, and the second version will be called when s is const. Value category has nothing to do with it.
Ref-qualification looks like this:
#include <iostream>
class SomeClass {
public:
int f() & { std::cout << "lvalue\n"; }
int f() && { std::cout << "rvalue\n"; }
};
int main() {
SomeClass s; s.f(); // prints "lvalue"
SomeClass{}.f(); // prints "rvalue"
}
Ofcourse the book is correct. Let me explain the workings of an example of what the author meant :
#include <iostream>
using namespace std;
class CO
{
int _m;
public:
CO(int m) : _m(m) {}
int& m() { return _m; } // used as an l-value
int const& m() const { return _m; } // used as an r-value
};
int main()
{
CO a(1);
cout << a.m() << endl;
a.m() = 2; // here used as an l-value / overload resolution selects the correct one
cout << a.m() << endl;
return 0;
}
Output is
1
2
What you misunderstood is the function signature. You see when you have an argument &arg (as in id(&arg)) you pretty much predefine the l-valuness of it, so returning it through a const or non const member function does not change a thing.
The author refers to a common writting style that allows for 'getters' and 'setters' to be declared with a signature different only in const qualifires yet compile and behave correctly.
Edit
To be more pedantic, the following phrase
Recall that an l-value means it can be used on the left-hand side of an assignment statement.
is not valid anymore. lr valuness applies to expressions, and the shortest way to explain it, is that an expression whose adress we can take, is an l-value; if it's not obtainable it's an r-value.
So the syntax to which the author refers to, enforces the member function to be used correctly (correct compilation / overload resolution) at both sides of the assignment operator. This nowdays is no longer relevant to lr valueness.
A const member function can only be called on a const object. It makes no difference what you do with the return value. In your example, f is non-const, so it always calls the non-const version of f(). Note that you can also overload on r-value references (&&) in C++11.

C++ adding friend to a template class in order to typecast

I'm currently reading "Effective C++" and there is a chapter that contains code similiar to this:
template <typename T>
class Num {
public:
Num(int n) { ... }
};
template <typename T>
Num<T> operator*(const Num<T>& lhs, const Num<T>& rhs) { ... }
Num<int> n = 5 * Num<int>(10);
The book says that this won't work (and indeed it doesn't) because you can't expect the compiler to use implicit typecasting to specialize a template.
As a soluting it is suggested to use the "friend" syntax to define the function inside the class.
//It works
template <typename T>
class Num {
public:
Num(int n) { ... }
friend
Num operator*(const Num& lhs, const Num& rhs) { ... }
};
Num<int> n = 5 * Num<int>(10);
And the book suggests to use this friend-declaration thing whenever I need implicit conversion to a template class type. And it all seems to make sense.
But why can't I get the same example working with a common function, not an operator?
template <typename T>
class Num {
public:
Num(int n) { ... }
friend
void doFoo(const Num& lhs) { ... }
};
doFoo(5);
This time the compiler complaints that he can't find any 'doFoo' at all.
And if i declare the doFoo outside the class, i get the reasonable mismatched types error. Seems like the "friend ..." part is just being ignored.
So is there a problem with my understanding? What is the difference between a function and an operator in this case?
The reason is that here
doFoo(5);
the compiler has no way of finding foo, given an int parameter. This would be the equivalent of calling your friend operator like this:
Num<int> n = 5 * 10;
This will "work", but not by calling the friend operator* defined in your Num class, but by calling the built-in operator* for integers, and then using the implicit conversion from Num's converting constructor.
The core problem is lookup. A friend declaration provides a declaration of a namespace level function, but the declaration is only available inside the class that is befriending it. In the example the book provides that is not an issue: the function takes two arguments of the enclosing type, as long as one of them is of the enclosing type, Argument Dependent Lookup will look inside the definition of the class and find the operator. In your case that is not the case, since there is a single argument and that needs a conversion, the compiler will not look inside the definition of the class.
Note that this is regardless of templates and conversions:
class A {
friend void f( int ) {}
friend void g( int, A ) {}
};
int main() {
f(5); // Error: lookup cannot find 'f' declared *only* inside A
g(5,A()); // Ok, one argument is 'A', lookup will find the function
}
In the case above, where there are no templates involved, you could potentially add a declaration at namespace level to fix it, but that is not really an option for template classes.
class A {
friend void f() { std::cout << "inside A\n"; }
};
void f(int); // only declaration
int main() {
f(5); // "inside A"
}
This cannot be done for a template (and for all instantiating types) as the friend declaration is a declaration of a non-templated function. Although you could can play with the code just for the sake of testing:
template <typename T>
struct Num {
Num(int x) ...
friend void f( Num const & );
};
Num<int> f(Num<int> const &); // only declaration
int main() {
f(5);
}
Yes these code compiler do not know how to work with it .
like
doFoo(5)
compiler do not know 5 is int

Why doesn't conversion work with custom operators in C++?

Consider the following code:
class C {
public:
int operator-(int x) {
return 3-x;
}
};
class wrapper {
public:
operator C() {
static C z;
return z;
}
} wrap;
int main() {
return wrap-3;
}
it gives this error on g++:
test.cpp: In function ‘int main()’:
test.cpp:17:17: error: no match for ‘operator-’ in ‘wrap - 3’
The conversion operator seems to be working because this version works:
class wrapper {
public:
operator int() {
static int z=3;
return z--;
}
} wrap;
int main() {
return wrap-3;
}
operator- also seems to be working because this code compiles:
class C {
public:
int operator-(int x) {
return 3-x;
}
};
int main() {
C c
return c-3;
}
What's wrong with the combination of these two? Why can't an operator be applied after implicit conversion? Are there any workarounds to this problem?
Implicit conversions aren't performed on the first operand when a member function is matched. Just make your operator a non-member, perhaps a friend:
class C {
};
int operator-(C c, int x) {
return 3-x;
}
From [over.match.oper]:
— If T1 is a complete class type, the set of member candidates is the result of the qualified lookup of T1::operator# (13.3.1.1.1); otherwise, the set of member candidates is empty.
It doesn't compile (using GCC) because it would neet to chain two user-defined conversions to get from wrapper to int: first to C, then to int. The standard doesn't allow this. See David Rodriguez' answer in another thread.
When you do return wrap-3; the compiler dosn't know to convert wrapper to a C in order
for the calculation to take place, it is looking for a operator- in wrapper, or a
conversion in wrapper to a numeric type. Just because C has a operator- dosn't make the
compiler implicitly convert to it, you could have multiple conversion operators in wrapper,
which one should the compiler convert to?
Either explicitly tell the compiler to convert to C, of add the operator- to wrapper like this..
class wrapper {
public:
operator C() {
static C z;
return z;
}
int operator-(int x) {
return C()-x;
}
} wrap;