infoclogged
infoclogged

Reputation: 4017

function template by value and by reference difference

I am following this tutorial - http://www.learncpp.com/cpp-tutorial/132-function-template-instances/

// passing all parameters by references
template <typename T1, typename T2>
const T2& add_two_objects(const T1& x,const T2& y) {
        return x+y;
};

int main() {
    using std::cout;
    int x(0),y(0);
    std::cout << "Please enter number 1" << std::endl;
    std::cin >> x;
    std::cout << "Please enter number 2" << std::endl;
    std::cin >> y;
    cout<< "sum of two integers is " << add_two_objects(x,y+1.2)    << '\n';
    cout<< "sum of two double is " << add_two_objects(x,y+2.52424324) << '\n';
    return 0;
}

The program compiles fine, but at the run time, I always get a segmentation fault. However if I change the template to pass by value, then everything works.

// passing all parameters by value
template <typename T1, typename T2>
const T2 add_two_objects(const T1 x,const T2 y) {
        return x+y;
};

Can anyone please explain?

Upvotes: 1

Views: 68

Answers (2)

Walter
Walter

Reputation: 45484

This

template <typename T1, typename T2>
const T2& add_two_objects(const T1& x, const T2& y) {
        return x+y;
};

returns a reference to a temporary. Make the return a value

template <typename T1, typename T2>
T2 add_two_objects(const T1& x, const T2& y) {
        return x+y;
};

and you should be fine.

Btw, it's not clear that returning a T2 is the best thing to do here. Consider for example the case T1=size_t and T2=char. Better to return the type that the operation x+y actually generates

template <typename T1, typename T2>
auto add_two_objects(const T1& x, const T2& y)
-> decltype(x+y)
{
    return x+y;
};

------edit-----

You must not return a reference to a temporary object. It's a BUG. If you want a bug, go for it. And you should not use poor tutorials. There are situations when you want/should return a reference, but this is not one of them. These are when the reference is to an object that will not be destroyed (or go out of scope) when returning form the function (as all automatic and temporary variables will).

Upvotes: 6

Vlad from Moscow
Vlad from Moscow

Reputation: 311156

To make it more clear let's wrap the integers in structures.

Here is a demonstrative program

#include <iostream>

struct A
{
    A( int x ) : x( x ) {}
    ~A() { std::cout << "[A::~A() is called for x = " << x << ']' << std::endl; }
    int x;
};

A operator +( const A &lhs, const A &rhs )
{
    return A( lhs.x + rhs.x );
}

std::ostream & operator <<( std::ostream &os, const A &a )
{
    return os << a.x;
}

template <typename T1, typename T2>
const T2& add_two_objects(const T1& x,const T2& y) {
        return x+y;
};

int main() 
{
    std::cout<< "sum of two integers is " << add_two_objects( A( 1 ), A( 2 ) ) << '\n';

    return 0;
}

Its output might look like

prog.cc:22:18: warning: returning reference to temporary [-Wreturn-local-addr]
         return x+y;
                  ^
sum of two integers is [A::~A() is called for x = 3]
Segmentation fault

First of all the compiler warns that the function returns reference to a temporary value. That is after exiting the function the temporary object will be destroyed and this output

[A::~A() is called for x = 3]

confirms this.

As result the reference will be invalid and the program has undefined behavior.

In fact you can imagine the program logic the following way

int main() 
{
    const A &r = add_two_objects( A( 1 ), A( 2 ) );
    std::cout<< "sum of two integers is " << r << '\n';

    return 0;
}

Its output looks almost similar as in the above program

prog.cc:22:18: warning: returning reference to temporary [-Wreturn-local-addr]
         return x+y;
                  ^
[A::~A() is called for x = 3]
[A::~A() is called for x = 1]
[A::~A() is called for x = 2]
Segmentation fault

That is the reference becomes invalid.

If to remove the reference in the function declaration

template <typename T1, typename T2>
const T2/*&*/ add_two_objects(const T1& x,const T2& y) {
        return x+y;
};

then the program output might look like

sum of two integers is 3
[A::~A() is called for x = 3]
[A::~A() is called for x = 1]
[A::~A() is called for x = 2]

Upvotes: 1

Related Questions