绑定到可变参数成员函数

时间:2018-08-27 22:48:10

标签: c++ variadic-templates crtp

这是这种情况:我有两个通过CRTP进行静态继承的类。基类有一个run方法,该方法使用可变参数模板调用派生方法,以便参数灵活。现在,派生类包含一个函数对象。派生类具有基类调用的实现。似乎没有必要,但是在此代码的完整版本中,运行的命令不仅仅是所包含的函数。接下来是通过绑定所有可变参数和实例到方法CrtpBase::Run的方法,将该函数转换为bool(void)函数。这是我遇到的问题。我尝试了两种不同的方法,使用lambda的版本已被注释掉。两种方法均无效。我的目标是让VoidFunction绑定所有参数,以便我可以在没有参数的情况下随意执行该函数。我在这里做什么错了?

#include <functional>
#include <utility>

template <typename D>
struct CrtpBase {
  template <typename ... Args>
  bool Run(Args&& ... args) const {
    return static_cast<D&>(*this).Impl(std::forward<Args>(args) ...);
  }
};

template <typename ... Args>
struct CrtpDerived : public CrtpBase<CrtpDerived<Args ...>> {
  CrtpDerived(std::function<bool(Args ...)> function) : runable(std::move(function)) {}

  bool Impl(Args&& ... args) const {
    return this->runable(std::forward<Args>(args) ...);
  }

  std::function<bool(Args ...)> runable;
};

template <typename D, typename ... Args>
std::function<bool()> VoidFunction(CrtpBase<D> base, Args&& ... args) {
//  return [&base, &args ...]()->bool{return CrtpBase<D>::template Run<Args ...>(base);};
  return std::bind(CrtpBase<D>::template Run<Args ...>, base, std::forward<Args>(args) ...);
}

int main(int argc, char** argv) {
  std::function<bool(int&)> fn = [](int& a)->bool{a /= 2; return (a % 2) == 1;};
  CrtpDerived<int&> derived(fn);
  int x = 7;
  auto voided = VoidFunction(derived, x);
  bool out = voided();
  if ((x == 3) and (out == true)) {
    return EXIT_SUCCESS;
  } else {
    return EXIT_FAILURE;
  }
}

编辑:

  1. 最终测试(out == false)中的固定错字变成了(out == true)

1 个答案:

答案 0 :(得分:1)

首先,从编译器的角度来看,CrtpBase<D>::template Run<Args ...>是令牌的荒谬/不完整的组合。 C ++中没有这样的表达语法。这看起来像是尝试形成一个指向成员的指针,但这需要显式应用&运算符

return std::bind(&CrtpBase<D>::template Run<Args ...>, base, std::forward<Args> (args) ...);

第二,这个演员

static_cast<D&>(*this)

将尝试放弃常数。 static_cast不允许这样做。

第三,您的

std::bind(&CrtpBase<D>::template Run<Args ...>, base, std::forward<Args> (args) ...);

将隐含的this参数绑定到功能参数base。这将行不通,因为base会在VoidFunction退出时(或在调用表达式结束时)被销毁。。正如@aschepler在通过base值传递CrtpBase<D>的注释中正确标注的那样,将原始CrtpDerived<int&>对象切成薄片。通过引用将其传递,然后使用&base作为std::bind的参数。

第四,std::bind will not bind "by reference"std::forward对此无济于事。这意味着您的a内部的fn将不会绑定到x。使用std::ref来解决该限制。

#include <functional>
#include <utility>

template <typename D>
struct CrtpBase {
  template <typename ... Args>
  bool Run(Args&& ... args) const {
    return static_cast<const D&>(*this).Impl(std::forward<Args>(args) ...);
  }
};

template <typename ... Args>
struct CrtpDerived : public CrtpBase<CrtpDerived<Args ...>> {
  CrtpDerived(std::function<bool(Args ...)> function) : runable(std::move(function)) {}

  bool Impl(Args&& ... args) const {
    return this->runable(std::forward<Args>(args) ...);
  }

  std::function<bool(Args ...)> runable;
};

template <typename D, typename ... Args>
std::function<bool()> VoidFunction(CrtpBase<D> &base, Args&& ... args) {
  return std::bind(&CrtpBase<D>::template Run<Args ...>, &base, std::forward<Args>(args) ...);
}

int main(int argc, char** argv) {
  std::function<bool(int&)> fn = [](int& a)->bool { a /= 2; return (a % 2) == 1; };
  CrtpDerived<int&> derived(fn);
  int x = 7;
  auto voided = VoidFunction(derived, std::ref(x));
  bool out = voided();
  if ((x == 3) && (out == false)) {
    return EXIT_SUCCESS;
  } else {
    return EXIT_FAILURE;
  }
}

最后一件事:我不明白您为什么期望您的out最终成为false