【问题标题】:How to write a function that takes an operator as argument with default values?如何编写一个将运算符作为参数并具有默认值的函数?
【发布时间】:2021-10-05 16:44:08
【问题描述】:

我想写一个函数模板,带有一个函数指针类型的可选参数,默认值为operator+。如果在定义了operator+ 的对象上调用该函数,则使用默认值。如果在没有预定义operator+ 的对象上调用该函数,则调用者可以指定一个函数来进行添加。这是我想做的一个例子:

#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>

template <typename T, typename fun=std::plus<T>>
T addT(T t1, T t2, fun f = {}) {
    return f(t1, t2);
}

int main()
{
    double a = 1., b= 2.;
    auto c = addT(a, b);  // use default value of std::plus
    std::cout<<c<<std::endl;
    
    using vec_int = std::vector<int>;
    vec_int x = {1,1}, y = {2,2};
    auto vec_int_add = [](vec_int x, vec_int y){
        vec_int res;
        std::transform(x.begin(), x.end(), y.begin(), std::back_inserter(res), [](int x, int y){return x+y;});
        return res;
    };
    auto z = addT(x, y, vec_int_add);  // supply a lambda as argument 
    std::cout<<z[0]<<','<<z[1]<<std::endl;
}

问题是默认值std::plus 仅在两个参数与上面示例的类型相同时才有效。我希望默认值是具有两种不同类型参数的函数,例如:

template<typename T>
T default_add(T x, double y) {
    T res = x + y;
    return res
}

我有点不知道该怎么做。

【问题讨论】:

  • 你不能只添加一个带有两个参数的重载吗? template &lt;typename T1, typename T2&gt; auto addT(T1 t1, T2 t2) { return t1 + t2; }
  • 您可以尝试约束fun(如果您使用c++20)以便requires(T a, T b, fun f) { f(a,b) } -&gt; std::same_as&lt;T&gt;} 或类似的东西。根据您的需要,您也可以大大扩展它。您也可以只将第二个参数转换为 T,但这可能会很糟糕,具体取决于您想要做的事情

标签: c++ operator-keyword function-templates


【解决方案1】:

在 C++14 中,您可以使用 std::plus&lt;void&gt;

template <typename T1, typename T2, typename fun = std::plus<void>>
auto addT(T1 t1, T2 t2, fun f = {}) {
    return f(t1, t2);
}

Demo

【讨论】:

    【解决方案2】:

    只需有两个参数的重载。

    #include <cstdlib>
    #include <vector>
    #include <algorithm>
    
    template<typename T1, typename T2>
    auto addT(T1 t1, T2 t2) {
        return t1 + t2;
    }
    
    template <typename T, typename fun = std::plus<T>> // but is this really needed?
    T addT(T t1, T t2, fun f) {
        return f(t1, t2);
    }
    
    template <typename T1, typename T2, typename fun>
    auto addT(T1 t1, T2 t2, fun f) {
        return f(t1, t2);
    }
    
    int main(){
        addT(1, 2);
        addT(1, 2.0); // returns promoted common type, double
        addT(1, 2, [](int a, int b) { return a * b; });
        addT(1.0, 2, [](double a, int b) { return a / b; });
    }
    

    【讨论】:

    • 您可以考虑在addT 中使用std::invoke(C++17 及更高版本)而不是常规调用,那么它也可以轻松地用于成员函数。
    • @JeJo 您的代码将导致addT(1, 2.0) 返回int。哦,我明白了,但也许这是 OP 的要求。
    • @JeJo: std::commont_type 错位了,因为addT(1, 0.5) 返回1.,你可能想要that
    【解决方案3】:

    std::plus&lt;T&gt; 替换为您自己的函子,该函子会添加并可以接受两种不同的类型:

    struct Adder {
        template<class T1, class T2>
        auto operator()(T1 v1, T2 v2) const noexcept { return v1 + v2; }
    };
    
    template <typename T, typename T2, typename fun=Adder>
    auto addT(T t1, T2 t2, fun f = {}) {
        return f(t1, t2);
    }
    

    【讨论】:

      【解决方案4】:

      你可以这样做:

      template <typename T1, typename T2, typename fun=std::plus<T>>
      T1 addT(T1 t1, T2 t2, fun f = {}) {
          return f(t1, (T1)t2);
      }
      

      这不是很好,因为您可能有一些类型为其他类型重载了operator+,但该类型不能转换为第一种类型。您可以使用概念来缩小范围,例如:

      #include <concepts>
      
      template <typename T1, typename T2, typename fun=std::plus<T>>
      requires std::convertible_to<T2,T1>
      T1 addT(T1 t1, T2 t2, fun f = {}) {
          return f(t1, (T1)t2);
      }
      template <typename T1, typename T2, typename fun>
      requires requires(T1 t1, T2 t2, fun f) {
          { f(t1,t2) } -> std::same_as<T1>;
      }
      T1 addT(T1 t1, T2 t2, fun f = {}) {
          return f(t1, t2);
      }
      

      这样你可以以某种方式保持一般性。重载解决方案可能会有点混乱,但您可能希望包括:

      template <typename T1, typename T2, typename fun>
      requires std::convertible_to<T2,T1> && requires(T1 t1, T2 t2, fun f) {
          { f(t1,t2) } -> std::same_as<T1>;
      }
      T1 addT(T1 t1, T2 t2, fun f = {}) {
          return f(t1, t2);
      }
      

      因此,如果两个约束都满足,则不进行强制转换。

      【讨论】:

        猜你喜欢
        • 2021-05-28
        • 2018-08-03
        • 2019-04-14
        • 2011-05-13
        • 2018-05-02
        • 2017-02-03
        • 1970-01-01
        • 2019-09-11
        • 1970-01-01
        相关资源
        最近更新 更多