繁体   English   中英

从可变模板类推导出类型包并声明相同类型包的参数

[英]Deduce types pack from a variadic-templated class and declare an argument of the same types pack

首先,很抱歉问题标题不清楚,如果您想出更好的表述方式,请随时进行编辑。

我有一堂课:

template <typename ...Arguments>
class CSignal
{
    template <typename ...ActualArguments>
    void invoke(ActualArguments&&... args) const {}
};

还有一个,这就是我遇到的问题:

class SomeClass
{
    template<typename ...Arguments>
    void invokeQueued(CSignal<Arguments...>& signal, const Arguments&... args)
    {
        m_queue.emplace_back([=](){signal.invoke(args...);});
    }

    std::deque<std::function<void (void)>> m_queue;
};

问题:

CSignal<float> signal;
int i = 0;
SomeClass().invokeQueued(signal, i);

错误:

template parameter 'Arguments' is ambiguous
could be 'float'
or       'int'

可能的天真解决方案

template<typename ...FormalArguments, typename ...ActualArguments>
void invokeQueued(CSignal<FormalArguments...>& signal, const ActualArguments&... args)
{
    m_queue.emplace_back([=](){signal.invoke(args...);});
}

是不是在这种特殊情况下可以接受的,因为我需要通过值来捕获的参数(将它们复制到拉姆达),并从转换ActualArgumentsFormalArguments必须在时间发生invokeQueued被调用,而不是当拉姆达被调用。

如果我可以typedef的参数在包装CSignal类,我会做:

template<typename ...FormalArguments>
void invokeQueued(CSignal<FormalArguments...>& signal, const CSignal<FormalArguments...>::argument_types&... args)
{
    m_queue.emplace_back([=](){signal.invoke(args...);});
}

但这似乎不可能。 解决方案?

您遇到的错误是因为参数的类型不同,而编译器对于每个这样的一对只有一个类型模板参数:从CSignal的签名它看到float ,而从第二个参数的推导类型它看到int ,并且两者都必须与Arguments包的单个元素匹配。 这就是歧义的由来。

要解决此问题,您可以通过引入非推导 context 来从模板参数推导中排除参数之一,例如使用以下标识技巧:

template <typename T> struct identity { using type = T; };
template <typename T> using identity_t = typename identity<T>::type;

class SomeClass
{
public:
    template <typename... Arguments>
    void invokeQueued(CSignal<Arguments...>& signal,
                      const identity_t<Arguments>&... args)
    //                      ~~~~~~~~~^
    {
        m_queue.emplace_back([=](){signal.invoke(args...);});
    }

    std::deque<std::function<void(void)>> m_queue;
};

编译器不会尝试推导出属于嵌套名称说明符语法一部分的任何模板参数,这基本上就是identity所做的 - 它引入了identity<T>::type语法,因此T留给作用域解析运算符,但是它仍然可以在函数声明中完整地使用。

演示


或者,您可以存储在 lambda 表达式 (C++14) 中捕获参数时转换为正确类型的参数的衰减副本:

#include <utility>
#include <type_traits>
#include <cstddef>

class SomeClass
{
public:
    template <typename... FormalArguments, typename... ActualArguments>
    void invokeQueued(CSignal<FormalArguments...>& signal, ActualArguments&&... args)
    {
        invokeQueued(signal, std::index_sequence_for<ActualArguments...>{}, std::forward<ActualArguments>(args)...);
    }

    template <typename... FormalArguments, typename... ActualArguments, std::size_t... Is>
    void invokeQueued(CSignal<FormalArguments...>& signal, std::index_sequence<Is...>, ActualArguments&&... args)
    {
        m_queue.emplace_back(
          [signal, t = std::tuple<std::decay_t<FormalArguments>...>(std::forward<ActualArguments>(args)...)]
          (){signal.invoke(std::get<Is>(t)...);});
    }

    std::deque<std::function<void(void)>> m_queue;
};

演示 2

使用标识技巧为Arguments&&...创建一个非推导的上下文

template <typename T>
class Identity
{
public:
using type = T;
};

template<typename ...Arguments>
void invokeQueued(CSignal<Arguments...>& signal, const typename Identity<Arguments>::type &... args)
{
    m_queue.emplace_back([=](){signal.invoke(args...);});
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM