【问题标题】:How to pass overloaded function to an operator?如何将重载函数传递给运算符?
【发布时间】:2012-07-28 02:24:26
【问题描述】:

我需要将函数传递给操作员。任何具有正确 arg 类型的一元函数。返回类型可以是任何东西。因为这是库代码,所以我无法将其包装或将f 强制转换为特定的重载(operator* 之外)。函数将operator* 第一个参数作为它自己的参数。下面的人工示例编译并返回正确的结果。但它硬编码了int 返回类型——以使这个示例编译。

#include <tuple>
#include <iostream>
using namespace std;

template<typename T>
int operator* (T x,  int& (*f)(T&) ) {
    return (*f)(x);
};

int main() {
    tuple<int,int>  tpl(42,43);
    cout << tpl * get<0>;
}

是否可以让operator* 接受具有任意返回类型的f

更新 - GCC 错误? 代码:

#include <tuple>

template<typename T, typename U> 
U operator* (T x,  U& (*f)(T&) ) {  
    return (*f)(x);
}; 

int main() {
    std::tuple<int,int>  tpl(42,43);
    return   tpl * std::get<0,int,int>;
}  

使用 gcc462 和 453 可以正确编译和运行,但使用 gcc471 和 480 时会被拒绝。因此可能是 GCC 回归错误。我已经提交了错误报告: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54111

编辑 我已将示例更改为使用元组作为 arg - 在前面的示例中可以简单地推断出返回类型。

EDIT2 很多人无法理解需要什么,所以我将call 函数更改为operator* 以使示例更真实。

【问题讨论】:

  • My question 可能对您有所帮助。它允许我通过someVar = wrap (funcName, arg1, arg2, arg3); 调用包装的变体。它也处理 void 返回类型。
  • 一般来说,类型推导和重载在 C++ 中有些矛盾。你已经走到了极限——更多的概括,然后没有足够的信息来确定要选择什么重载。
  • 编辑完全改变了问题,您应该为此创建一个不同的问题,顺便说一句,它无法解决,因为get&lt;0&gt; 不是一个函数,std::get 是一个可变参数模板,从中在使用过程中可以推断除第一个参数之外的所有参数,但是,如果不向函数提供参数,您必须手动提供所有模板参数,此时您不需要推断,因为您可以从手动的参数中获取它传递给get 模板...
  • @DavidRodríguez - 不,get&lt;0&gt; 就足够了。示例 BTW 编译并运行并返回正确的值。 get&lt;0&gt; 参数在 call 签名中指定。在前面的例子中,f 也是模板。

标签: c++ templates c++11 operator-overloading function-pointers


【解决方案1】:

是的,如果这就是你的意思:

template<typename T, typename F> 
auto call (T x, F f) -> decltype(f(x)) {  
    return (f)(x); 
}

实际上有很多方法可以做到这一点。

【讨论】:

  • f 可以重载。这意味着应该指定 f arg 类型。
  • @LeonidVolnitsky 那么你必须在将它作为参数传递之前将它转换为你想要的类型的函数指针。
  • 或将其包装在 lambda 中,这让一切变得容易得多。
  • 我已经澄清了我的问题(参见重载段落)。我无法将 wrap 预先转换为 lambda。
  • @Xeo,你能发表一下你所说的包装是什么意思吗?
【解决方案2】:

作为对您更新问题的回答:

正如@DavidRodríguez 所讨论的,get&lt;0&gt; 是不够的,语法上正确的&amp;get&lt;0&gt; 也是不够的。你需要的是&amp;get&lt;0,int,int&gt;。按照您的示例,它将是:

#include <tuple>
using namespace std;

template<typename T, typename U>
U call (T x, U (*f)(T&) ) {
      return (*f)(x);
};

int main() {
    tuple<int,int>  tpl(42,43);
    call(tpl, &get<0,int,int>);
    return 0;
}

正常使用std::get&lt;&gt;()时,会自动推导出int,int部分。但是在您的情况下,您需要提供它,因为没有参数。一种解决方法是自定义get 模板函数:

#include <tuple>
using namespace std;

template <size_t I, typename T>
auto myGet(T& tpl) -> decltype(get<I>(tpl))
{
    return get<I>(tpl);
}

template<typename T, typename U>
U call (T x, U (*f)(T&) ) {
      return (*f)(x);
};


int main() {
    tuple<int,int>  tpl(42,43);
    auto get0 = &myGet<0, decltype(tpl)>;
    call(tpl, get0);

//  call(tpl, &myGet<0, decltype(tpl)>); // all in one line, do not work
    return 0;
}

【讨论】:

  • 如果还不够,我的示例将无法编译/运行。您的第一个代码示例被 gcc48 拒绝
  • @Leonid,但你的例子不能做你想做的事:接受f的任意返回类型。我正在使用 gcc 4.6.3 并且第一个代码被接受。无论如何,第二个代码怎么样?
  • 我没有尝试第二个代码 - 正如我在问题中解释的那样:我无法包装或投射 f。我没有 gcc463,但我尝试过 gcc453 和 gcc471。第一次 - 编译和运行正确(你得到 +1),第二次 - 被拒绝。我将使用 gcc 提交错误报告。
  • 我正在提交错误报告:我的代码是否可以使用 gcc463 正确编译和运行?
  • @LeonidVolnitsky:UPDATE - GCC bug 下的代码:使用 gcc 463 编译正常。
【解决方案3】:

你应该可以这样做:

template<typename T,typename U>
U call (T x, U (*f)(T) ) {
      return (*f)(x);
};

【讨论】:

  • 它编译(你从我这里得到了 +1)。但这并不是我想要的。我的错——不好的例子。我已将其更改为使用元组作为 arg,现在您的方法将不起作用。
  • @Xeo:不 - get 就足够了。 不需要 - 可以推导出来。无法包裹。我可以处理重载的左值/右值。
  • @Xeo:你低估了我 :) 在call 签名中指定了f args 类型。这足以提取特定的everload。示例编译并运行。
  • @Leonid:TIL:C++ 的模板参数推导比我想象的要强大。我完全忘记了模板参数推导的上下文信息。 Small test case。我还在纳闷,你为什么不能只接受一个F f 模板化参数?
  • @Xeo: F f 不会提取重载 --> 由于不明确而无法编译。
猜你喜欢
  • 1970-01-01
  • 2021-10-02
  • 2012-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-16
  • 1970-01-01
相关资源
最近更新 更多