I have the following code with overloaded template functions
#include <iostream>
using namespace std;
template <class T>
const T& max(const T& a1, const T& a2)
{
cout << "general template" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
const T* max(const T* a1, const T* a2)
{
cout << "max for pointers" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
const T& max(const T& a1, const T& a2, const T& a3)
{
cout << "general template with three parameters" << endl;
return ::max(::max(a1, a2), ::max(a1, a2));
}
int main()
{
int* a = new int(5);
int* b = new int(56);
int* c = new int(2);
int*const &g = ::max(a, b, c);
cout << *g << endl;
return 0;
}
I was expecting it to fail, because the max template with three parameters would return reference to a temporary variable (returned by template for pointers). But it works and calls general template function.
The question is why doesn't called template for pointers?
Thanks.
If you comment out the definition of 2-argument max for references, you would see that the code does not compile. The error that MSVC++2013 gives at line 22 is:
error C2440: 'return' : cannot convert from 'const int *' to 'int *const &'
That seems to be the reason why max for references is always selected: template substitution fails for max for pointers.
If you change the code to the following, then template for pointers is called:
#include <iostream>
using namespace std;
template <class T>
T max(const T& a1, const T& a2)
{
cout << "general template" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
T* max(T* a1, T* a2)
{
cout << "template for pointers" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
T max(const T& a1, const T& a2, const T& a3)
{
cout << "general template with three parameters" << endl;
return ::max(::max(a1, a2), a3);
}
int main()
{
int* a = new int(5);
int* b = new int(56);
int* c = new int(2);
int* g = ::max(a, b, c);
cout << *g << endl;
return 0;
}
If you comment out the definition of the template for pointers, then the template for references is called. The preference of template for pointers over template for references seems to happen because the type is already a pointer.
Here is some explanation on the order of template matching: What are the rules for choosing from overloaded template functions?
EDIT: the OP's code can be altered in another way, so that MSVC++2013 prefers reference-based template to pointer-based:
#include <iostream>
using namespace std;
template <class T>
T max(const T& a1, const T& a2)
{
cout << "general template" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
const T* max(const T* a1, const T* a2)
{
cout << "template for pointers" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
T max(const T& a1, const T& a2, const T& a3)
{
cout << "general template with three parameters" << endl;
return const_cast<const T>(::max(::max(a1, a2), a3));
}
int main()
{
int* a = new int(5);
int* b = new int(56);
int* c = new int(2);
int* g = ::max(a, b, c);
cout << *g << endl;
return 0;
}
This happens because in this version pointer-based template definition has additional qualifiers for its parameter types: they are not just T*, but rather const T*.
It's not working. It seems to be working, because you're only using the first two parameters or your three parameter max function.
template <class T>
const T& max(const T& a1, const T& a2, const T& a3)
{
cout << "general template with three parameters" << endl;
return ::max(
::max(a1, a2),
::max(a1, a2)); // HERE
}
Correcting that shows what is going on: You're comparing pointer addresses.
See here in action.
Your pointer overload isn't called because the reference version is a better fit: a1 etc ARE const references (to pointers, but well). So the reference version is a perfect match on respect to overload resolution.
I guess to achieve what you want you'd need some SFINAE magic here.
One thing I'd like to add: since comparing pointers is a frequent operation in C++, IMHO it would be very misleading to have some max dereference the pointers before comparing.
When you call max(a, b, c),T in max(const T& a1, const T& a2, const T& a3) becomes an aliases of int *,so in max(const T& a1, const T& a2, const T& a3)
max(a,b) will match max(const T& a1, const T& a2)
typedef int * T;
const T x;
const int * y; //they are different
Related
I'm not able to understand the scope operator in Operator overloading. There are examples when they are using it when they don't. When I'm supposed to write T::operator. Can I write just the operator still works fine or is it recommended to use::?
The example:
Prototype examples (for class T)
Inside class definition
T& T::operator +=(const T2& b){}
Can I write it like T& operator +=(const T2& b){}
Or should I always write it like T& T::operator +=(const T2& b){}
The operator += may be declared as a member function or member template of a class or class template and defined either within the class or class template or outside them.
If it is defined outside the class then the scope operator is required.
The operator may be also declared and defined as a stand-alone non-class function
Consider the following demonstrative program
#include <iostream>
struct A
{
int x = 0;
A & operator +=( char c )
{
x += c;
return *this;
}
};
struct B
{
int x = 0;
B & operator +=( char c );
};
B & B::operator +=( char c )
{
x += c;
return *this;
}
struct C
{
int x = 0;
};
C & operator +=( C & cls, char c )
{
cls.x += c;
return cls;
}
int main()
{
A a;
a += 'A';
std::cout << "a.x = " << a.x << '\n';
B b;
b += 'B';
std::cout << "b.x = " << b.x << '\n';
C c;
c += 'C';
std::cout << "c.x = " << c.x << '\n';
return 0;
}
Its output is
a.x = 65
b.x = 66
c.x = 67
The operator can be also declared as a template operator. For example
#include <iostream>
template <class T>
struct A
{
T x = T();
};
template <class T1, class T2>
T1 & operator +=( T1 &a, const T2 &x ) /* C++ 17 only requires requires( T1 t ) { t.x; }*/
{
a.x += x;
return a;
}
int main()
{
A<int> a;
std::cout << ( a += 10u ).x << '\n';
}
Again if the operator is a member function template and is defined outside its class then the scope resolution operator is required.
#include <iostream>
template <class T1>
struct A
{
T1 x = T1();
template <class T2>
A<T1> & operator +=( const T2 &x );
};
template <class T1>
template <class T2>
A<T1> & A<T1>::operator +=( const T2 &x )
{
this->x += x;
return *this;
}
int main()
{
A<int> a;
std::cout << ( a += 10u ).x << '\n';
}
Inside the class, you don't use the scope resolution operator :::
class T {
public:
// ...
T operator+=(const T2& b)
{
// ...
}
};
If you define the operator outside the class, then you use the scope resolution operator :: in the out of class definition. You still omit it in the declaration:
class T {
public:
// ...
T operator+=(const T2& b);
};
// in the implementation
T T::operator+=(const T2& b)
{
// ...
}
This has nothing to do with recommendation or good practice. Everything stated here is the only way that can work, the other ways are just not correct C++ code.
I am trying to modify the Add function to represent operator overloading.
#include <iostream>
using namespace std;
template <class T>
class cpair
{
public:
cpair(T x = 0, T y = 0) { A = x; B = y; }
void print()
{
cout << A << " " << B << endl;
}
T A, B;
};
template <class T>
void Add(cpair<T> A1, cpair<T> A2, cpair<T> &R)
{
R.A = A1.A + A2.A;
R.B = A1.B + A2.B;
}
int main()
{
cpair<int> A1(4, 5), A2(1, 3), result;
Add(A1, A2, result);
result.print();
return 0;
}
I am learning operator overloading, but I don't think I have implemented it correctly. The error I get is:
'operator= must be a member function'.
template <class T>
void operator=(const cpair<T> &A1, cpair<T> &A2, cpair<T> &R) {
R.A = A1.A + A2.A;
R.B = A1.B + A2.B;
}
int main()
{
cpair<int> A1(4, 5), A2(1, 3), result;
operator(A1, A2, result);
result.print();
}
How do you go about modifying the Add function to represent operator overloading and then call the function in Main? Thank you.
You're misunderstanding quite a lot it seems. First of all if you want to overload the addition operator it's the operator+ function you need to overload, not the assignment operator.
To fix this you should do e.g.
template <class T>
cpair<T> operator+(cpair<T> const& a, cpair<T> const& b)
{
return cpair<T>(a.A + b.A, a.B + b.B);
}
Secondly, if you overload an operator you can use it just like you would otherwise use it. For example, with e.g.
int a = 5, b = 7, r;
then you would do
r = a + b;
It's the same with your overloaded operators:
cpair<int> a(4, 5), b(1, 3), result;
result = a + b;
If you want to learn more I suggest you get a few good books to read.
template <class T>
class cpair {
public:
cpair& operator+=( cpair const& o )&{
A+=o.A;
B+=o.B;
return *this;
}
friend cpair operator+( cpair lhs, cpair const& rhs ){
lhs+=rhs;
return lhs;
}
//...
the above is the canonical way to override + on a template class.
I have this program with 2 template functions :
#include <iostream>
template <class T> void assign(T& t1,T& t2){
std::cout << "First method";
t1=t2;
}
template <class T> void assign(T& t1,const T& t2) {
std::cout << "Second method";
t1=t2;
}
class A
{
public:
A(int a):_a(a){};
private:
int _a;
friend A operator+(const A& l, const A& r);
};
A operator+(const A& l, const A& r) {
return A(l._a+r._a);
}
int main ()
{
A a=1;
const A b=2;
assign(a,a+b);
}
i cant understand why does the assign(a,a+b) call the second template function
, in the operator+ we are createing a new A object and call the ctor with an int parameter.
it is creating a+b as const object ?
it is creating a+b as const object ?
No, it is creating a temporary. Temporaries are binded to rvalue references. You can verify that with a 'third' function (a universal reference, in this case)
template <class T>
void assign(T& t1, T&& t2) {
std::cout << "Third method";
t1=t2;
}
As you don't have one, the compiler will pick the const reference overload. Why ?
Suppose you have
void add(int & x)
{
++x;
}
unsigned y = 0;
add(y); // create a temporary to int
std::cout << y << "\n"; // what's the value of y ?
Given a template and a more specialized overload:
template <typename T>
const T& some_func(const T& a, const T& b)
{
std::cout << "Called base template\n";
return (a < b) ? a : b;
}
template <typename T>
T* const& some_func(T* const& a, T* const& b)
{
std::cout << "Called T* overload\n";
return (*a < *b) ? a : b;
}
Then the following works as expected:
int main()
{
std::cout << some_func(5.3, 6.2) << "\n";
double a = 1;
double b = 2;
double *p = &a;
double *q = &b;
const double *ret = some_func(p, q);
std::cout << *ret << "\n";
return 0;
}
With the first printing Called base template, the second printing Called T* overload. If we replace the overload signature with:
template <typename T>
const T*& some_func(const T*& a, const T*& b)
then the second call now calls the base template. Given that int const& x is equivalent to const int& x, am I incorrect in assuming T* const& is equivalent to const T*&? Why is the first version resolved properly while the second is not?
Yes you are incorrect. In const T*& T is const, in T* const& the pointer (T*) is const.
The change happens when you shift const to the right of *. const T*& and T const *& are equivalent but T* const& is different. Typedefs can help, given
typedef T *TPtr;
const TPtr& is equivalent to TPtr const & is equivalent to T* const &.
Declarators in C/C++ are hard to parse (for a human). Even the inventor of C said so.
T const & and const T & are identical; and U const * and const U * are identical.
But S & and T const & are not the same even for T = S. What you have is even worse, namely S = U const * and T = U *.
I have the following C++ code:
#include <iostream>
template <class T>
void assign(T& t1, T& t2) {
std::cout << "First method" << std::endl;
t1 = t2;
}
template <class T>
void assign(T& t1, const T& t2) {
std::cout << "Second method" << std::endl;
t1 = t2;
}
class A {
public:
A(int a) : _a(a) {};
private:
int _a;
friend A operator+(const A& l, const A& r);
};
A operator+(const A& l, const A& r) {
return A(l._a + r._a);
}
int main() {
A a = 1;
const A b = 2;
assign(a, a);
assign(a, b);
assign(a, a+b);
}
The output is:
First method
Second method
Second method
The output stays the same even if I comment out the the first 2 assigns in the main function.
Can someone please explain to me why the operator+ returns a const A?
The output is the same in both Linux Debian 64bit and Windows 7 64 bit.
It doesn't return a const A at all. It returns a temporary A, which may only bind to a const reference.