【问题标题】:Container of variadic template class in C++C++中可变参数模板类的容器
【发布时间】:2020-04-23 14:46:46
【问题描述】:

我正在尝试为 std::functions 编写一个包装器,其返回类型为 void 和可变数量的 std::string 参数。

template<class... Args>
class ConsoleCommand
{
    public:
        ConsoleCommand(const std::string& name, const std::function<void(Args...)>& function) :
            name{ name },
            function{ function }
        {

        }

        void operator()(Args... args)
        {
            function(std::forward<Args>(args)...);
        }
    private:
        std::string name;
        std::function<void(Args...)> function;
};

所以这是正确的,除了函数的 arity 必须根植于实例的类型中,我试图避免这种情况。

ConsoleCommand<std::string> ex1; //unary function
ConsoleCommand<std::string, std::string> ex2; //dyadic function

我想要一个包含这些对象的容器 (std::vector&lt;ConsoleCommand&lt;std::string&gt;&gt;),如果我可以让类模板参数只引用参数的类型,而不是整个包,那就太好了。

template<class Args>
class ConsoleCommand
{
    public:
        ConsoleCommand(const std::string& name, const std::function<void(Args...)>& function) :
            name{ name },
            function{ function }
        {

        }

        void operator()(Args... args)
        {
            function(std::forward<Args>(args)...);
        }
    private:
        std::string name;
        std::function<void(Args...)> function;
};

我试过了,但是没有用。对于我想要实现的目标,是否有任何解决方法?

【问题讨论】:

  • 您想如何使用该包装器?这个问题似乎不完整
  • 您可能有别名让ConsoleCommandN&lt;string, 4&gt; 等同于ConsoleCommand&lt;string, string, string, string&gt;
  • 这在您使用std::function 的方式下是不可能的。 std::function 不能不知道它将采用多少个参数。您可能希望使用any 之类的类型擦除类型或span 之类的可变大小视图类型来代替std::function 的参数类型。

标签: c++ templates variadic-templates


【解决方案1】:

您可以考虑使用类型擦除,这是在运行时类型信息/多态性下隐藏模板的常用方法。

基本上你有一个没有/很少功能的虚拟基类和一个实现存储/访问的派生类。

然后你可以使用虚方法或者dynamic_cast来访问模板化的派生类。

这里有一个例子来匹配你的问题:

class ConsoleCommand
{
private:
    // Dummy virtual base class, used to hide the template behind RTTI
    struct FunctionWrapperBase
    {
        // Needs to be virtual to enable RTTI
        virtual ~FunctionWrapperBase() {}
    };

    // Concrete type
    template<class... Args>
    struct FunctionWrapperConcrete : FunctionWrapperBase
    {
        // Just holds the function
        FunctionWrapperConcrete(std::function<void(Args...)> function)
        : function(function) {}

        std::function<void(Args...)> function;
    };

    std::string name;
    // (smart) Pointer to the virtual wrapper class
    std::unique_ptr<FunctionWrapperBase> erased_wrapper;

    // This calls the function, outside operator() so we ensure Args are all strings
    template<class... Args>
    void call_all_strings(Args... args)
    {
        auto concrete = dynamic_cast<FunctionWrapperConcrete<std::decay_t<Args>...>*>(erased_wrapper.get());
        if ( concrete )
            concrete->function(args...);
    }

public:
    template<class... Args>
    ConsoleCommand(const std::string& name, const std::function<void(Args...)>& function) :
        name{ name },
        erased_wrapper{ std::make_unique<FunctionWrapperConcrete<Args...>>(function) }
    {

    }

    template<class... Args>
    void operator()(Args&&... args)
    {
        call_all_strings(std::string(args)...);
    }
};

你甚至可以扩展它,这样你就可以存储任何可调用的(不仅仅是 std::function),因为这类似于 std::function 的实现方式

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-28
    • 1970-01-01
    • 2015-06-08
    • 1970-01-01
    • 1970-01-01
    • 2020-07-31
    • 1970-01-01
    • 2023-04-02
    相关资源
    最近更新 更多