【问题标题】:Binding to a variadic member function绑定到可变成员函数
【发布时间】:2018-08-27 22:48:09
【问题描述】:

所以情况是这样的:我有两个通过 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)

【问题讨论】:

    标签: c++ variadic-templates crtp


    【解决方案1】:

    首先,从编译器的角度来看,CrtpBase&lt;D&gt;::template Run&lt;Args ...&gt; 是一个无意义/不完整的标记组合。 C++ 中没有这样的表达式语法。这看起来像是试图形成一个指向成员的指针,但这需要显式应用 &amp; 运算符

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

    其次,这个演员表

    static_cast<D&>(*this)
    

    将尝试抛弃 constness。 static_cast 不允许这样做。

    第三,你的

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

    将隐含的this 参数绑定到函数参数base。这是行不通的,因为base 将在VoidFunction 退出(或调用表达式结束后)立即销毁。正如@aschepler 在将base 传递为CrtpBase&lt;D&gt; 值的cmets 中正确指出的那样,对原始CrtpDerived&lt;int&amp;&gt; 对象进行了切片。通过引用传递它,然后使用&amp;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

    【讨论】:

    • 那么,我应该如何解决这个问题?我可以静态转换为const D&amp;。这就说得通了。它编译。但是当它运行时,它会抛出一个 std::bad_function_call 的实例,所以某处仍然存在问题。
    • 我猜我得到的 std::bad_function_call 是因为 base 被破坏了。为什么会这样,我该如何解决?
    • @esdanol:是的,这就是原因。请参阅上面的更正代码。它按预期运行,除了 out 最后是 true(应该是这样)。
    • 啊,是的,这是我的错字。我将编辑代码并记下它,以防其他人看到这篇文章。非常感谢!
    • 您提到base 被销毁,并将其更正为固定代码中的引用,但我还要指出非引用基类变量发生的对象切片。跨度>
    猜你喜欢
    • 2012-08-07
    • 1970-01-01
    • 2019-12-24
    • 1970-01-01
    • 2015-12-08
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多