C++ 如何使用'std::function'作为函数参数创建可变模板函数?

C++ 如何使用'std::function'作为函数参数创建可变模板函数?,c++,c++11,variadic-templates,std-function,C++,C++11,Variadic Templates,Std Function,如何使用std::function创建可变模板函数作为接受可变参数数的函数参数?我试图将问题简化为MWE: #include <functional> template <class T> void run(std::function<void(T *)> fun, T *obj) { fun(obj); } template <class T, class... Args> void run_variadic(std::function<

如何使用
std::function
创建可变模板函数作为接受可变参数数的函数参数?我试图将问题简化为MWE:

#include <functional>

template <class T> void run(std::function<void(T *)> fun, T *obj) { fun(obj); }

template <class T, class... Args>
void run_variadic(std::function<void(T *, Args...)> fun, T *obj, Args... args) {
  fun(obj, args...);
}

struct Foo {
  void bar() {}
};

int main() {
  Foo foo;
  std::function<void(Foo *)> fun = &Foo::bar;

  run(fun, &foo);                     // works
  run<Foo>(&Foo::bar, &foo);          // works
  run_variadic(fun, &foo);            // works
  run_variadic<Foo>(&Foo::bar, &foo); // does not compile
}
关于如何修复
run\u variadic
以便不必遍历额外的
std::function
对象,有什么建议吗

背景

我有一个类层次结构

template <class T> class Abstract { ... };
class UnrelatedStuff { ... };
class Derived : public Abstract<UnrelatedStuff> { ... };
所有循环都应该是OpenMP加速的。我希望在使用循环的
Derived
的每个方法中,加速器的内容都被分解,而不是重复,这样,如果OpenMP切换到OpenACC,我只需更改一个位置


因此,我正在寻找一种将循环(及其装饰)置于其自身功能中的方法。将其移动到
抽象
基类也不是一个选项,因为循环是性能关键的,我不能在每个循环迭代中都有一个抽象函数调用。

您几乎总是最好抽象掉函数对象:

template <class Functor, class... Args>
void run(Functor&& f, Args&&... args) {
  f(std::forward<Args>(args)...);
}
std::function
std::bind
相比,我更喜欢lambda,但如果它们已经可用,您也可以使用它们:

run(std::function<void(Foo *)>{&Foo::bar}, &foo);
run(std::bind(&Foo::bar, &foo));
run(std::mem_fn(&Foo::bar), foo);
然后可以为每个执行策略实现
的重载,例如:

namespace detail {

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::serial_t) {
  boost::for_each(std::forward<RandomAccessRange>(r), std::forward<Functor>(f));
}

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::openmp_t) {
  #pragma omp parallel for
  for (auto&& v : r) { f(v); } 
}

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::openacc_t) {
  #pragma acc parallel for
  for (auto&& v : r) { f(v); } 
}

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::tbb_t) {
  tbb::parallel_for_each(std::begin(std::forward<RandomAccessRange>(r)),
                         std::end(std::forward<RandomAccessRange>(r)),
                         std::forward<Functor>(f)); 
}

}  // namespace detail
这将至少为您提供一个高效的TBB后端,即使OpenMP/OpenACC的性能充其量只是一般。您可以查看libstdc++的并行实现,其中使用OpenMP。他们的
for_每个
算法都有1000多行代码,并使用工作窃取

完整示例程序:

#include <functional>

template <class Functor, class... Args>
void run(Functor&& f, Args&&... args) {
  f(std::forward<Args>(args)...);
}

struct Foo { void bar() {} };

int main() {
  Foo foo;

  run([&](auto... args) { foo.bar(args...); } /*, bar takes no args*/);
  run(std::function<void(Foo *)>{ &Foo::bar}, &foo);
  run(std::bind(&Foo::bar, &foo));
  run(std::mem_fn(&Foo::bar), foo);
}
#包括
模板
无效运行(函子和函数、参数和…参数){
f(标准:正向(参数)…);
}
结构Foo{void bar(){};
int main(){
富富,;
运行([&](auto…args){foo.bar(args…;}/*,bar不接受args*/);
运行(std::function{&Foo::bar},&Foo);
运行(std::bind(&Foo::bar,&Foo));
运行(std::mem_fn(&Foo::bar),Foo);
}

要回答您对上一个答案的评论,该答案可以按照您要求的方式进行调整,以支持指向成员函数的指针。前面的答案已经适用于所有可调用的对象,但不能直接使用指向成员函数的指针,因为这些对象不能用通常的
f(args)
语法调用。以下版本使用标记分派来区分指向成员函数和传统可调用对象的指针,并应用适合于每种情况的调用语法

template <class Functor, class... Args>
void run_helper(std::false_type, Functor f, Args&&... args)
{
    f(std::forward<Args>(args)...);
}

template <class Functor, class Arg0, class... Args>
void run_helper(std::true_type, Functor f, Arg0&& arg0, Args&&... args)
{
    (std::forward<Arg0>(arg0).*f)(std::forward<Args>(args)...);
}

template <class Functor, class... Args>
void run(Functor f, Args&&... args)
{
    run_helper(typename std::is_member_pointer<Functor>::type(),
               f, std::forward<Args>(args)...);
}
如果显式实例化
run
模板以绑定到特定的重载函数或函数模板实例化,它甚至可以处理重载成员函数和作为模板的成员函数


实例:

这只是一个MWE,在实际代码中,
run
方法包含一个并行循环,并在每个循环迭代中调用
fun
。您的问题似乎是编译器在尝试将成员函数指针映射到
std::function
时不知道
Args…
是什么,因为
Args…
还没有被推导出来(它在更右边)。不要这样使用
std::function
。它不起作用,即使它起作用,也不会给您带来什么好处。此外,提供的参数不太可能与
函数的参数完全匹配。您可能会有额外的限定符和转换等等。@MooingDuck,但即使您使用
std::decay\u t
或一些
unqualified\u t
特性来解决这个问题,您仍然会遇到麻烦。我想lambda语法是C++14?还有,有没有办法让这个方法更简洁?我必须在不同的类中多次调用
run
,我不希望每次都重复相同的lambda模式(或
std::bind
等)。@MichaelSchlottke使用
std::mem_fn
创建函数对象。如果需要类型擦除,还可以将其存储在
std::function
中一次,然后在任何地方使用。如果不需要类型擦除,则可以编写函数对象而不是普通函数。如果需要将普通函数包装为函数对象。。。lambda非常好,尤其是对于成员函数,因为它们需要以某种方式存储
this
指针,并使用特殊语法调用(
*
->*
)。如果没有更多关于你想要实现什么的细节,很难给你更好的建议。@MichaelSchlottke回答你的第一个评论:这是一个变量C++14λ,用于你的变量函数(我假设你需要从你的例子中绑定一个变量函数)。您可以使用
std::bind
在C++11中模拟它,它返回一个多态函数对象,如果您将一个可变成员函数绑定到一个对象,则该对象可以接受任意数量的参数。我更新了我的问题,以提供有关我试图实现的目标的更多详细信息。Upvote,因为这是另一种选择。这里的缺点是,如果
run
做了一些非同寻常的事情,您可能会在
run\u helper
中复制功能。如果没有关于OP试图实现什么的更多信息,就很难推荐一个替代方案,而不是另一个:/在C++17中,您可以使用它处理所有可调用对象,包括指向成员的指针。
template<class RandomAccessRange, class Functor, 
         class ExecutionPolicy = execution::serial_t>
void for_each(RandomAccessRange&& r, Functor&& f, 
              ExecutionPolicy&& ex = ExecutionPolicy{}) {
  detail::for_each_(std::forward<RandomAccessRange>(r), 
                    std::forward<Functor>(f), 
                    std::forward<ExecutionPolicy>(ex));
}
namespace detail {

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::serial_t) {
  boost::for_each(std::forward<RandomAccessRange>(r), std::forward<Functor>(f));
}

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::openmp_t) {
  #pragma omp parallel for
  for (auto&& v : r) { f(v); } 
}

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::openacc_t) {
  #pragma acc parallel for
  for (auto&& v : r) { f(v); } 
}

template<class RandomAccessRange, class Functor>
void for_each(RandomAccessRange&& r, Functor&& f, execution::tbb_t) {
  tbb::parallel_for_each(std::begin(std::forward<RandomAccessRange>(r)),
                         std::end(std::forward<RandomAccessRange>(r)),
                         std::forward<Functor>(f)); 
}

}  // namespace detail
namespace execution {
  struct serial_t {}; static const constexpr serial_t serial{};
  struct openmp_t {}; static const constexpr openmp_t openmp{};
  struct openacc_t {}; static const constexpr openacc_t openacc{};
  struct tbb_t {}; static const constexpr tbb_t tbb{};
}  // namespace execution
#include <functional>

template <class Functor, class... Args>
void run(Functor&& f, Args&&... args) {
  f(std::forward<Args>(args)...);
}

struct Foo { void bar() {} };

int main() {
  Foo foo;

  run([&](auto... args) { foo.bar(args...); } /*, bar takes no args*/);
  run(std::function<void(Foo *)>{ &Foo::bar}, &foo);
  run(std::bind(&Foo::bar, &foo));
  run(std::mem_fn(&Foo::bar), foo);
}
template <class Functor, class... Args>
void run_helper(std::false_type, Functor f, Args&&... args)
{
    f(std::forward<Args>(args)...);
}

template <class Functor, class Arg0, class... Args>
void run_helper(std::true_type, Functor f, Arg0&& arg0, Args&&... args)
{
    (std::forward<Arg0>(arg0).*f)(std::forward<Args>(args)...);
}

template <class Functor, class... Args>
void run(Functor f, Args&&... args)
{
    run_helper(typename std::is_member_pointer<Functor>::type(),
               f, std::forward<Args>(args)...);
}
run(&Foo::bar, foo);