我可以让 C++ 编译器决定是按值传递还是按引用传递?

Posted

技术标签:

【中文标题】我可以让 C++ 编译器决定是按值传递还是按引用传递?【英文标题】:Can I let the C++ compiler decide whether to pass-by-value or pass-by-reference? 【发布时间】:2012-11-04 16:42:45 【问题描述】:

看看这个假设的头文件:

template <class T>
class HungryHippo 
public:
    void ingest(const T& object);
private:
    ...

现在,对于HungryHippo&lt;string&gt;,您希望ingest 引用字符串是有道理的——复制字符串可能非常昂贵!但是对于HungryHippo&lt;int&gt;,它的意义不大。直接传递int 可能非常便宜(大多数编译器会在寄存器中进行),但是传递对int 的引用是额外不必要的间接级别。这也适用于返回值。

有没有办法向编译器建议“嘿,我不会修改参数,所以决定是通过值传递还是通过引用传递,这取决于你的想法更好”?

一些可能相关的事情:

我可以通过写template &lt;class T, bool PassByValue&gt; class HungryHippo 手动伪造这个效果,然后专门处理PassByValue。如果我想变得非常花哨,我什至可以根据sizeof(T)std::is_trivially_copyable&lt;T&gt; 推断出PassByValue。无论哪种方式,当实现看起来几乎相同时,这都是很多额外的工作,我怀疑编译器可以比我更好地决定是否通过值传递。 libc++ 项目似乎通过内联很多函数来解决这个问题,因此编译器可以做出更上一层楼的选择,但在这种情况下,假设ingest 的实现相当复杂,不值得内联。 如 cmets 中所述,所有模板函数默认为 inline

【问题讨论】:

需要考虑的一点:这听起来像是一个过早优化的好案例。您是否因为这种架构而遇到过性能问题?如果没有,你可能应该做任何语义上有意义的事情。 对于整数,通过引用传递几乎与通过值传递一样高效,因此 const 引用对这两种情况都有好处。 "在这种情况下,假设摄取的实现相当复杂,不值得内联。"在这种情况下,您别无选择。由于 HungryHippo 是模板,所以必须内联。 @Slavik81 这根本不是真的。 我想知道为什么编译器(和 ABI 规范)不只是将const int &amp; 视为与int 相同。 (对于所有通过值传递比通过 const 引用更有效的内置类型也是如此。)它会破坏任何东西吗? 【参考方案1】:

boost::call_traits 标头正好解决了这个问题。看看here。

具体来说,call_traits&lt;T&gt;::param_type 选项包括以下描述:

如果T 是一个小的内置类型或指针,则定义param_type 作为T const,而不是T const&amp;。这样可以提高能力 编译器优化函数体中的循环(如果它们依赖) 在传递参数上,传递参数的语义是 否则不变(需要部分特化)。

在你的情况下,你可以定义ingest如下:

template <class T>
class HungryHippo 
public:
    void ingest(call_traits<T>::param_type object);
    // "object" will be passed-by-value for small 
    // built-in types, but passed as a const reference 
    // otherwise
private:
    ...
;

这是否真的会对您的实际代码/编译器组合产生很大影响,我不确定。与往常一样,您必须运行一些实际的基准测试,看看会发生什么......

【讨论】:

像往常一样,Boost 人已经想到了这一点。 :D 看起来简短的回答是“不,没有办法诱使编译器为你做这件事”,而长的回答是“嗯,Boost 在某些情况下设计了一种解决该问题的方法。”谢谢!【参考方案2】:

虽然提到的诸如 boost 之类的技巧 call_traits&lt;T&gt; 在这种情况下可以做到他们声称要做的事情,但我认为您假设编译器尚未在最重要的情况下进行此优化。毕竟,这是微不足道的。如果您接受const T&amp;sizeof(T) &lt;= sizeof(void*),C++ 引用语义强加的不变量允许编译器简单地替换整个函数体中的值(如果它是胜利的话)。如果不是,最坏情况的开销是函数序言中的一个指向 arg 的指针取消引用。

【讨论】:

我不相信你所说的“C++ 引用语义允许编译器简单地替换整个函数体中的值”。考虑pastebin.com/xqyrQtxW ...根据您的规则,输出将为10,但根据real规则,输出 5。您不能替换const X& 和 X。正如我在之前的评论中所说,“const”意味着你不能改变值,而不是值不能改变。 @Calvin 实际上 Matthew Hall 是对的。函数可以采用 X 而不是 const X& 但在某些特定条件下 - 例如。当您无法确定 X 的地址(它是右值)时,您仍然可以将其传递给采用 const X& 的函数,这很有效。参见示例ideone.com/6KCeve 是的,在某些情况下这种优化是合法的。然而,由于它不是总是合法的(见我的例子),你几乎不会看到编译器这样做。如果过程是内联的并且可以在内联后消除引用,则更常见的是这种情况。

以上是关于我可以让 C++ 编译器决定是按值传递还是按引用传递?的主要内容,如果未能解决你的问题,请参考以下文章

Ruby 是按值传递还是按引用传递? [复制]

JS基础类型和对象,分别是按值传递还是按引用传递?

JavaScript 是按引用传递还是按值传递语言?

java中的参数传递是按引用传递还是按值传递

JavaScript 是按引用传递还是按值传递? [复制]

Objective-C 对象是按值传递还是按引用传递?