绑定到可变成员函数

Binding to a variadic member function

情况是这样的:我有两个 classes 通过 CRTP 进行静态继承。基础 class 有一个 运行 方法,该方法使用可变参数模板调用派生方法,因此参数很灵活。现在派生的 class 包含一个函数对象。派生 class 具有由基 class 调用的实现。这似乎是不必要的,但在这段代码的完整版本中,除了包含的函数之外,还有更多命令 运行。接下来有一个方法通过将所有可变参数和实例绑定到方法 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)

首先,从编译器的角度来看 CrtpBase<D>::template Run<Args ...> 是一个 nonsensical/incomplete 的标记组合。 C++ 中没有这样的表达式语法。这看起来像是试图形成指向成员的指针,但这需要显式应用 & operator

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。这是行不通的,因为一旦 VoidFunction 退出(或调用表达式结束)base 就会被销毁。正如@aschepler 在评论中正确指出的那样,将 base 作为 CrtpBase<D> 值传递给原始 CrtpDerived<int&> 对象。通过引用传递它,然后使用 &base 作为 std::bind.

的参数

第四,std::bindwill not bind "by reference"std::forward不会帮你解决这个问题。这意味着您的 lambda fn 中的 a 不会绑定到 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