【问题标题】:template pass by value or const reference or...?模板通过值或常量引用传递或...?
【发布时间】:2011-06-20 02:24:24
【问题描述】:

我可以这样写一个模板函数

template<class T> void f(T x) {…}

或者这样

template<class T> void f(T const& x) {…}

我猜第二个选项可能更优化,因为它明确地避免了复制,但我怀疑它对于某些特定类型T(例如仿函数?)也可能失败。 那么,什么时候应该使用第一个选项,什么时候使用第二个呢?还有这个boost::call_traits&lt;T&gt;::param_typeboost::reference_wrapper 在我的previous question 的答案中,但人们不会到处使用它们,是吗?这有经验法则吗?谢谢。

【问题讨论】:

  • 冒着看起来很愚蠢的风险,我必须问:函子怎么会失败?
  • @Beta:这是一个随机猜测。如果有人能解释在哪些情况下它实际上会失败,我会很感兴趣。
  • 你想要达到什么目的?这取决于f中的内部代码。
  • @David Rodríguez - dribeas:这是一个更普遍的问题

标签: c++ templates pass-by-reference pass-by-value


【解决方案1】:

这有经验法则吗?

适用于何时使用按引用传递和按值传递的相同一般规则。

如果您希望T 始终是数字类型或复制起来非常便宜的类型,那么您可以按值获取参数。如果您无论如何要将参数复制到函数中的局部变量中,那么您应该将其按值传递给help the compiler elide copies that don't really need to be made.

否则,通过引用获取参数。对于复制成本较低的类型,它可能会更昂贵,但对于其他类型,它会更快。如果您发现这是一个性能热点,您可以为不同类型的参数重载该函数,并为每个参数做正确的事情。

【讨论】:

  • 那么根本就没有模板特定的问题吗?
  • @7vies: 不是真的;唯一的“问题”是,您必须决定要使用模板的类型组,而不是只考虑一种类型。
  • @James McNellis:我想有时你无法决定这一点,例如 STL 会按值接受函子,而可能有一个“重状态”函子,那将是低效的。在这种情况下应该使用 boost::call_traits&lt;T&gt;::param_type,还是像 STL 那样坚持按值传递?
  • @7vies: boost::call_traits 不会帮助你,因为param_type 是任何用户定义类型(因此也是所有类类型)的常量引用。 STL 中的假设是函数对象的复制成本很低;如果您有一个复制成本高的函数对象,那么“正确的解决方案”是尽可能降低复制成本,或者编写一个包装器以降低复制成本。
  • @James McNellis:我假设(尽管从未尝试过)如果应该按值传递,您可以为您的类型专门化该特征。
【解决方案2】:

我怀疑某些特定类型也可能失败

通过引用到常量传递是唯一“永不”失败的传递机制。它对T 没有任何要求,它接受左值和右值作为参数,并且允许隐式转换。

【讨论】:

    【解决方案3】:

    你不应该唤醒死者,但会遇到类似的问题,这里有一些示例代码,展示了如何使用 C++11s 类型特征来推断参数应该按值传递还是按引用传递:

    #include <iostream>
    #include <type_traits>
    
    template<typename key_type>
    class example
    {
        using parameter_type = typename std::conditional<std::is_fundamental<key_type>::value, key_type, key_type&>::type;
    
     public:
      void function(parameter_type param)
      {
          if (std::is_reference<parameter_type>::value)
          {
              std::cout << "passed by reference" << std::endl;
          } else {
              std::cout << "passed by value" << std::endl;
          }
      }
    };
    
    struct non_fundamental_type
    {
        int one;
        char * two;
    };
    
    int main()
    {
      int one = 1;
      non_fundamental_type nft;
    
      example<int>().function(one);
      example<non_fundamental_type>().function(nft);
    
      return 0;
    }
    

    希望它可以帮助其他有类似问题的人。

    【讨论】:

      【解决方案4】:

      除了 James McNellis 写的内容之外,我只想补充一点,您可以针对引用类型专门化您的模板 (for example like this)

      【讨论】:

        【解决方案5】:

        boost::traits 有一个类型特征,可以根据 T 选择“最佳”类型:

        call_traits&lt;T&gt;::param_type

        如前所述,不存在特定于模板的问题。

        【讨论】:

        • 在这种情况下,通常编译器无法推断出模板参数(这是特定于模板的问题)。
        猜你喜欢
        • 2013-09-10
        • 2013-08-01
        • 2015-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-27
        • 2016-05-04
        相关资源
        最近更新 更多