【问题标题】:How to restrict the parameter type of a callable to be passed to a function? [duplicate]如何限制可调用的参数类型传递给函数? [复制]
【发布时间】:2020-08-26 03:56:53
【问题描述】:
#include <functional>
template <typename M>
M g(std::function<M(int)> f) {
    return f(0);
}

int main() {
    g([](int x){return x + 1;});
    return 0;
}

我想表达类似“传递给函数g 的(唯一)参数应该是一个以int 作为参数类型的可调用对象”。

G++ 9.3.0 说

prog.cc: In function 'int main()':
prog.cc:8:31: error: no matching function for call to 'g(main()::<lambda(int)>)'
    8 |     g([](int x){return x + 1;});
      |                               ^
prog.cc:3:3: note: candidate: 'template<class M> M g(std::function<M(int)>)'
    3 | M g(std::function<M(int)> f) {
      |   ^
prog.cc:3:3: note:   template argument deduction/substitution failed:
prog.cc:8:31: note:   'main()::<lambda(int)>' is not derived from 'std::function<M(int)>'
    8 |     g([](int x){return x + 1;});
      |                               ^

上述尝试有什么问题?我应该如何实现这个意图?

您可能希望在 Wandbox here 上查看代码 sn-p。

【问题讨论】:

  • 你为什么觉得你需要这样强制执行?
  • 您希望传入的函数以int 作为参数类型,还是接受任何可以使用int 调用的函数?
  • 为什么要这样限制?与template &lt;typename M&gt; auto f(M m) { return m(0); } 相比,它到底给你带来了什么? coliru.stacked-crooked.com/a/3a9e94caf357f0b9
  • @n.'pronouns'm。我想强制m的参数类型正好是int,并禁止f([](double x) { // .... });这样的调用。
  • @cigien 我希望传入的函数具有int 作为参数的类型。

标签: c++


【解决方案1】:

首先,编写一个简单的元函数,给出一元函数指针的参数类型:

template<class Ret, class Arg>
auto arg(Ret(*)(Arg)) -> Arg;

然后,在g 的主体中,您可以在static_assert 中表示传入的可调用对象(当衰减为函数指针时)具有int 类型的单个参数:

template <typename Function>
auto g(Function f) { 
    static_assert(std::is_same_v<decltype(arg(+f)), int>);
    return f(0);
}

现在对g 的调用只有在满足此约束时才会编译:

int a(int x) { return x + 1; }
int b(double x) { return x + 1; }

int main() {
    g([](int x){return x + 1;});      // ok
    g([](char x){return x + 1;});     // error
    g([](int x, int){return x + 1;}); // error
    g(a);                             // ok
    g(b);                             // error
}

这是demo

【讨论】:

  • 并非每个可调用对象都会衰减为函数指针,
  • @n.'pronouns'm。是的,我认为这不适用于成员函数。是否还有其他情况不起作用?如果可以的话,我会尝试修复它。
  • 你可以为成员函数重载,它们很简单。捕获 lambda 和其他函数对象(包括 std::function)。更有问题。您需要提取他们的operator() 并检查其类型,并且可能存在重载或模板化的operator()
【解决方案2】:

与直觉相反,您实际上应该使用std::function 来获取函数参数。相反,您只需要一个普通的模板参数。

使用纯模板参数适用于std::function。但它也适用于函数指针,并允许函数使用 lambda(或任何其他具有 operator()) 直接,没有std::function 的性能损失。

template <typename F>
auto g(F f) -> decltype(f(std::declval<int>())) { //alternatively, `decltype(f(0))`
    return f(0);
}

如果f 没有可以将表达式0 作为输入的成员函数,则编译将失败,因此强制执行约束。无论您是否包含-&gt; decltype 部分,实际上都是这种情况。


实际上回答标题中的问题有点棘手。有两件事在起作用:

第一个是 lambda 表达式不是std::function 实例化,即使它可以包装在其中。给定的具体 std::function 实例化,例如 std::function&lt;int(int)&gt;,有一个构造函数,该构造函数将从 lambda 转换,这就是我们通常使用 std::function 的方式。

但是,隐式转换不能很好地与模板配合使用。函数参数用于推导模板参数,在这种情况下,函数参数必须匹配,或者函数参数是完全具体的,在这种情况下可以执行隐式转换。但不是两者都有,这是您尝试做的。

【讨论】:

    【解决方案3】:

    下面的代码对我有用

    #include <iostream>
    #include <functional>
    
    template <typename M>
    M g(std::function<M (int)> f) {
      return f(0);
    }
    
    int main() {
      std::cout << g<int>([](int x){return x + 1; });
      return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2014-05-22
      • 2022-01-22
      • 2020-02-21
      • 2012-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-18
      • 1970-01-01
      相关资源
      最近更新 更多