c++ - 将函数应用于元组中的每个元素,将每个元素转换为类型包中的不同类型,然后作为参数包传递

标签 c++ templates c++17 variadic-functions

我正在构建一个复杂的可扩展系统。细节并不重要,但我真的很喜欢这个设计,除了这个问题。

我有一个接口(interface) WithState<T> : Subject对于某些类型的 T。

有问题的类是在 template <typename... StateTypes> 上模板化的.

它拥有一个 std::tuple<std::shared_ptr<WithState<StateTypes>>...>> .

我有一个函数 std::any getStateFor(std::shared_ptr<Subject>) (其中 WithState<T> : Subject )。

我还有一个函数void handleStates(StateTypes... states) (此时不妨采用一个元组,无论哪个更容易)

现在我需要将所有这些部分连接在一起:我需要将元组中的元素向上转换为 shared_ptr<Subject> , 然后申请 getStateFor按顺序和 std::any_cast 添加到这些元素中的每一个结果顺序为StateTypes... , 然后将所有这些作为参数包一次转发到 handleStates .

(在这被标记为 XY 问题之前:更高层次的抽象不关心具体的状态类型,而我想以尽可能多的类型安全来实现较低的部分。到目前为止,这种方法看起来不错我的需求)

我可以通过将我的元组转换为 vector 来做到这一点,应用 getStateFor在每个上,然后编写一个应用适当的递归函数 any_cast ,但是我仍然不知道如何将我的结果收集到具有不同类型的元组中。我想知道这是否适用于智能折叠表达式...

这是现有代码的框架:

#include <memory>
#include <tuple>
#include <any>
#include <iostream>
#include <cassert>
#include <functional>

// ignoring references and const for brevity

class Subject { 
public:
  /* deleted copy assignment and constructor */ 
  // this is here so that the example works
  virtual std::any getState() = 0;
};

template <typename T> class WithState : public Subject { };

template <typename... StateTypes>
class StateHandler {
public:   
  std::tuple<std::shared_ptr<WithState<StateTypes>>...> subjects;
  // this one is actually in another class, but it doesn't matter
  void handleStates(StateTypes... states);

  void handleStatesForSubjects(std::function<std::any (std::shared_ptr<Subject>)> getStateFor) {
    // how do I implement this?
  }
};

int main() {
  struct foo { int a; int b; };

  struct WithInt : public WithState<int> { 
    std::any getState() override { return 17; } 
  };
  struct WithFoo : public WithState<foo> {
    std::any getState() override { return foo { 1, 2 }; }
  };

  StateHandler<int, foo> handler;

  handler.subjects = { 
    std::make_shared<WithInt>(), std::make_shared<WithFoo>() };

  handler.handleStatesForSubjects([](auto subj) { return subj->getState(); });
}

on godbolt

但是,这仍然缺少std::any_cast s 到具体类型。

有什么想法吗?

最佳答案

主要问题是您的元组包含 std::shared_ptr<WithState<StateTypes>>...这就是apply将尝试调用您给定的 lambda,但 lambda 只需要 StateTypes&&... .

还有一些改变可以让它工作,但首先是工作:

https://godbolt.org/z/hyFBtV

  • 我改变了getStateFor成为一个模板函数,您可以在其中指定您期望的类型:

    template<class StateType>
    StateType getStateFor(std::shared_ptr<Subject> s)
    {
        if (auto withState = std::dynamic_pointer_cast<WithState<StateType>>(s))
            return withState->getValue();
        throw "AHHH";
    }
    

    您仍然可以将其分配给 std::any如果需要(或提供返回 std::any 的非模板化重载)。但出于此处的目的,转换为 std::any和 back 只是不必要的开销 - 如果有的话,它是 less 类型安全的。

  • 您的 lambda 参数是 StateTypes&&... withStates .除了类型本身需要不同之外,&&除非您实际向 lambda 提供临时变量(std::apply 不会这样做),否则不起作用,或者如果您通过 auto&& 进行类型推导,则不起作用(这是不同的)。在代码中,lambda 表达式是这样的(为简单起见,我按值取值,您可以按 [const] 引用取值):

        [this](std::shared_ptr<WithState<StateTypes>>... withStatePtrs) {
            handleStates(getStateFor<StateTypes>(withStatePtrs)...);
        }
    
  • 您也不需要 dynamic_pointer_caststd::shared_ptr<WithState<T>>std::shared_ptr<Subject> .

  • 您的元组类型有一个额外的 > .


编辑:使用更新后的问题中的代码段,您会得到:

https://godbolt.org/z/D_AJ1n

相同的注意事项仍然适用,您只需要输入 any_cast在那里:

void handleStatesForSubjects(std::function<std::any(std::shared_ptr<Subject>)> getStateFor)
{
  std::apply(
    [this, getStateFor](std::shared_ptr<WithState<StateTypes>>... withStates) {
      handleStates(std::any_cast<StateTypes>(getStateFor(withStates))...);
    },
    subjects
  );
}

它可能比您预期的更直接,但您只需记下每个元素的操作:调用 getStateFor , 然后 any_cast它。对每个可变参数重复 ( ... )。你不需要任何 dynamic_pointer_cast或类似 - std::shared_ptr<Derived>可隐式转换为 std::shared_ptr<Base> .

关于c++ - 将函数应用于元组中的每个元素,将每个元素转换为类型包中的不同类型,然后作为参数包传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58630183/

相关文章:

c++ - 无法在我的新手 Makefile 中包含目录

c++ - 可以在 C++ 模板中强制使用连续的分配边界吗?

c++ - unseq 执行策略是否要求迭代器的 value_type 为 Cpp17CopyAssignable?

c++ - 高效的感知重要点 (PIP) 算法。在 R 或 Rcpp 中

c++ - g++ 链接器找不到 const 成员函数

C++:在基类中使用带有泛型虚函数的异步回调

C++98 中关于 __sync_synchronize() 问题的 C++0x 原子实现

c++ - std::pair 作为模板<class> 参数? C++

c++ - 为什么结构化绑定(bind)禁用 RVO 并继续返回语句?

c++ - bool 内存高效链表