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

Can I let the C++ compiler decide whether to pass-by-value or pass-by-reference?

本文关键字:按值传递 按引用传递 决定 C++ 编译器 我可以      更新时间:2023-10-16

看看这个假设的头文件:

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

现在,对于HungryHippo<string>,您希望ingest引用字符串是有道理的——复制字符串可能非常昂贵!但对于HungryHippo<int>来说,这就没那么有意义了。直接传递int可能非常便宜(大多数编译器会在寄存器中进行),但传递对int的引用是一种额外的不必要的间接级别。这一切同样适用于返回值。

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

一些可能相关的事情:

  • 我可以通过编写template <class T, bool PassByValue> class HungryHippo然后专门处理PassByValue来手动伪造这种效果。如果我想得到真正的幻想,我甚至可以根据sizeof(T)std::is_trivially_copyable<T>推断出PassByValue。无论哪种方式,当实现看起来几乎相同时,这都是大量的额外工作,我怀疑编译器在决定是否传递值方面比我做得更好
  • libc++项目似乎通过内联许多函数来解决这个问题,这样编译器就可以进行更高级别的选择,但在这种情况下,假设ingest的实现相当复杂,不值得内联如注释中所述,默认情况下,所有模板函数都是inline

boost::call_traits标头正是处理这个问题。在这里查看。

具体而言,call_traits<T>::param_type选项包括以下描述:

如果T是小型内置类型或指针,则定义param_type作为T const而不是T const&。这可以提高编译器优化函数体中的循环(如果它们依赖)对于传递的参数,传递的参数的语义为否则保持不变(需要部分专业化)。

在您的情况下,您可以如下定义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提到的call_traits<T>这样的技巧在这种情况下会做他们声称要做的事情,但我认为你假设编译器在最重要的情况下还没有进行这种优化。毕竟,这是微不足道的。如果接受const T&sizeof(T) <= sizeof(void*),C++引用语义强加的不变量允许编译器简单地替换整个函数体中的值(如果成功的话)。如果不是,那么最坏情况下的开销是指向函数prolog中arg取消引用的一个指针。