从可变模板类推导类型包,并声明相同类型包的参数
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...);});
}
在这种特定情况下是不可接受的,因为我需要按值捕获参数(将它们复制到 lambda 中(,并且从 ActualArguments
到 FormalArguments
的转换必须在调用 invokeQueued
时发生,而不是在调用 lambda 时发生。
如果我能在CSignal
类中typedef
参数包,我会这样做:
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
包的单个元素匹配。这就是歧义的来源。
要解决此问题,您可以通过引入非推导上下文从模板参数推导中排除其中一个参数,就像下面的标识技巧一样:
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...);});
}
相关文章:
- ISO C++禁止声明没有类型的"setInputNombre"
- 有没有办法一次声明相同类型的多个对象,并通过一个表达式立即使用相同的右值初始化它们?
- 将类型声明为类型模板参数的模板参数的一部分是否合法?
- C++需要所有声明的类型说明符和预期的非限定 id(需要调试帮助)
- 为<vtkDataArray> VTK 非结构化网格声明 vtkSmartPointer 类型的变量时出现问题
- 如何声明不同类型的模板化类之间的转换
- c++17通过生成预先声明的类型列表的笛卡尔乘积来生成std::变体
- 从其他类声明类类型变量会给出 C2143 错误(在"*"之前缺少";")
- 声明中类型和对象的相同标识符
- 如果使用返回引用的函数初始化"auto"var,为什么它不声明引用类型?
- 我在 c++ 中声明字符串类型时遇到问题
- 使用 basic_string 声明新类型的字符串
- 将前向声明的类型转换为无效是否合法?
- C++错误:ISO C++禁止声明.没有类型
- 在类中创建向量(2D 数组)的向量 - Error:C++ 需要所有声明的类型说明符
- 为什么要在将 2d 数组传递给函数时将其声明为类型,为什么要在类型中包含行和列
- Opencv C++ 声明一个类型为 uint8 的矩阵
- 引用和指针混淆部分(声明及其类型)
- ISO C++禁止声明没有类型[-fpermission]的"transpose"
- 推断声明的类型