【问题标题】:How can I overload a function with a callable object as a parameter based on the object's call signature?如何根据对象的调用签名重载具有可调用对象作为参数的函数?
【发布时间】:2018-04-09 18:52:24
【问题描述】:

例如,给定以下代码

class A {
 public:
    double operator()(double foo) {
        return foo;
    }
};

class B {
 public:
    double operator()(double foo, int bar) {
        return foo + bar;
    }
};

我想编写fun 的两个版本,一个适用于具有 A 签名的对象,另一个适用于具有 B 签名的对象:

template <typename F, typename T>
T fun(F f, T t) {
    return f(t);
}

template <typename F, typename T>
T fun(F f, T t) {
    return f(t, 2);
}

我期待这种行为

A a();
B b();
fun(a, 4.0);  // I want this to be 4.0
fun(b, 4.0);  // I want this to be 6.0

当然前面的例子在编译时会抛出一个模板重定义错误。

如果 B 是一个函数,我可以将fun 改写成这样:

template <typename T>
T fun(T (f)(T, int), T t) {
    return f(t, 2);
}

但我希望fun 能够同时处理函数和可调用对象。使用 std::bindstd::function 可能会解决问题,但我使用的是 C++98,而这些是在 C++11 中引入的。

【问题讨论】:

  • 这不可能。单个可调用对象可以有多个签名(例如重载的operator())。
  • 类似的东西在 C++ 中是可能的>=11 但 98 似乎太弱了。
  • 我认为this question 正在完成类似的事情,但我发现代码太混乱了。 @n.m。我真的不明白具有多个签名的可调用对象如何影响模板推导。
  • 链接的答案基于 sizeof(value-returned-by-call),当目标函数返回 void 时,它会崩溃。但是,如果这对您不重要,则可以使用这种方法。
  • 等等,我想我有办法了,等等……

标签: templates overloading c++98 method-signature callable-object


【解决方案1】:

这是一个从 this question 修改的解决方案,以适应返回 void 的函数。解决方案就是使用sizeof(possibly-void-expression, 1)

#include <cstdlib>
#include <iostream>

// like std::declval in c++11
template <typename T>
T& decl_val();

// just use the type and ignore the value. 
template <std::size_t, typename T = void> 
struct ignore_value {typedef T type;};

// This is basic expression-based SFINAE.
// If the expression inside sizeof() is invalid, substitution fails.
// The expression, when valid, is always of type int, 
// thanks to the comma operator.
// The expression is valid if an F is callable with specified parameters. 
template <class F>
typename ignore_value<sizeof(decl_val<F>()(1),1), void>::type
call(F f)
{
    f(1);
}

// Same, with different parameters passed to an F.
template <class F>
typename ignore_value<sizeof(decl_val<F>()(1,1),1), void>::type
call(F f)
{
    f(1, 2);
}

void func1(int) { std::cout << "func1\n"; }
void func2(int,int) { std::cout << "func2\n"; }

struct A
{
    void operator()(int){ std::cout << "A\n"; }
};

struct B
{
    void operator()(int, int){ std::cout << "B\n"; }
};

struct C
{
    void operator()(int){ std::cout << "C1\n"; }
    void operator()(int, int){ std::cout << "C2\n"; }
};

int main()
{
    call(func1);
    call(func2);
    call(A());
    call(B());
    // call(C()); // ambiguous
}

在 c++98 模式下使用 gcc 和 clang 检查。

【讨论】:

  • 这比我要求的还要多!非常感谢!
猜你喜欢
  • 2011-08-21
  • 2013-01-27
  • 1970-01-01
  • 1970-01-01
  • 2015-02-26
  • 1970-01-01
  • 1970-01-01
  • 2013-07-01
  • 1970-01-01
相关资源
最近更新 更多