按值和按引用差异的函数模板

Posted

技术标签:

【中文标题】按值和按引用差异的函数模板【英文标题】:function template by value and by reference difference 【发布时间】:2017-06-16 16:21:21 【问题描述】:

我正在关注本教程 - 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;

程序编译得很好,但在运行时,我总是遇到分段错误。但是,如果我将模板更改为按值传递,那么一切正常。

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

谁能解释一下?

【问题讨论】:

您不能返回对临时对象的引用。但这是毫无意义的代码,而且写得也很糟糕。也许你应该找一个不同的教程? @CodyGray 至少有一个 using std::cout 而不是 using namespace std; 我修改了教程例子来尝试更复杂的东西(当然是为了理解)我觉得教程还不错。 【参考方案1】:

这个

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

返回对临时对象的引用。将返回值设为值

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

你应该没事的。

顺便说一句,目前尚不清楚返回T2 是不是最好的做法。例如,考虑T1=size_tT2=char 的情况。最好返回操作x+y实际生成的类型

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

    return x+y;
;

-----编辑-----

不得返回对临时对象的引用。这是一个BUG。如果你想要一个错误,那就去吧。你不应该使用糟糕的教程。在某些情况下您想要/应该返回参考,但这不是其中之一。这些是指对从函数返回时不会被销毁(或超出范围)的对象的引用(就像所有自动和临时变量一样)。

【讨论】:

但我想返回一个参考,而不是您的第一个解决方案建议的值。是不是说明教程代码错了? @infoclogged 你想返回对什么的引用?在方法范围内对变量的引用在方法范围之外是没有用的 好的,我现在明白了。如果我实例化一个静态对象,那么引用就不再指向一个临时对象。因此,我可以在函数模板中声明一个静态变量( static T2 z; ),而不是返回 (x+y) ,然后返回引用( z = x+y; return z ; )。当然这不是真正的代码,只是为了理解基础知识。【参考方案2】:

为了更清楚,让我们将整数包装在结构中。

这是一个演示程序

#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;

它的输出可能看起来像

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

首先编译器警告函数返回一个临时值的引用。即退出函数后临时对象将被销毁并输出

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

证实了这一点。

因此引用将无效并且程序具有未定义的行为。

其实你可以这样想象程序逻辑

int main() 

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

    return 0;

它的输出看起来和上面的程序差不多

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

即引用失效。

如果要删除函数声明中的引用

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

那么程序输出可能看起来像

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]

【讨论】:

是否有任何特殊原因导致结构出现警告,但ints 没有? @tobi303 原因可能是结构体有构造函数和析构函数。

以上是关于按值和按引用差异的函数模板的主要内容,如果未能解决你的问题,请参考以下文章

C#基础按值和按引用传递参数

Python按值传递参数和按引用传递参数

Java按值调用和按引用调用的区别

带你深入理解传递参数

VB 参数传递:按值传递和按地址传递

带你深入理解传递參数