首先,对不清楚的问题标题表示抱歉,如果您想要一个更好的说明方式,请随时编辑。
我有一个班级:
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时发生。
如果我可以在typedef
课程中CSignal
参数包,我会这样做:
template<typename ...FormalArguments>
void invokeQueued(CSignal<FormalArguments...>& signal, const CSignal<FormalArguments...>::argument_types&... args)
{
m_queue.emplace_back([=](){signal.invoke(args...);});
}
但似乎不可能。溶液
答案 0 :(得分:8)
引发的错误是因为参数类型不同而编译器每个这样的一对只有一个类型模板参数:从它看到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;
};
答案 1 :(得分:1)
使用标识技巧为Arguments&&...
创建non-deduced context。
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...);});
}