Variadic 模板参数包推导失败
Variadic template parameter pack deduction failure
我正在尝试元编程。当我尝试将 variadic types
与 std::tuple
连接时,我遇到了一个问题。
#include <tuple>
template<typename... Args , typename... TupleArgs>
void f( Args&&... , const std::tuple<TupleArgs...>& )
{ }
template<typename... Args , typename... TupleArgs>
void f2( const std::tuple<TupleArgs...>& , Args&&... )
{ }
int main(){
auto t = std::make_tuple( 1 , 2 , 2.0 , 3.0f );
// f ( 1 , 2 , t ); // deduction/substition failed
f2( t , 1 , 2 ); // Ok, no problem
}
我直觉上希望这两个函数都能编译无误,但编译器似乎对此有所抱怨。
有人可以解释为什么 f
会给出 deduction/substitution 错误而 f2
不会吗?
是否有解决方法(我确定有)?
编译器:x86-x64 GCC 9.2 带有 -std=c++17 选项
f
失败,因为编译器无法判断您传递给它的元组参数 t
是 Args&&...
列表的一部分还是 const std::tuple<TupleArgs...>&
实例(在本例中是一个 SINGLE 元组,恰好具有可变类型)。
f2
的第一个参数实际上是一个单一的类型(元组)后面跟着一个可变参数包Args&&...
,所以你在第一个参数之后传递的任何参数显然都属于Args&&...
.请注意,我指的是实际函数的参数,而不是模板声明。函数原型是导致此行为的原因。
如果你想在一个函数参数列表中有两个参数包,那么你可以做一些技巧,比如把两个都放在元组中,或者把一个放在模板模板参数(嵌套模板)中。
我提供了两个示例解决方案。我更改了 f
以使用两个元组,并创建了一个使用模板模板参数的新函数 f3
。
#include <tuple>
template<typename... Args, typename... TupleArgs>
void f(const std::tuple<Args...>&, const std::tuple<TupleArgs...>&)
{ }
template<typename... TArgs, template <typename...> class T, typename... TupleArgs>
void f3(T<TArgs...>&, const std::tuple<TupleArgs...>&)
{ }
template<typename... Args, typename... TupleArgs>
void f2(const std::tuple<TupleArgs...>&, Args&&...)
{ }
int main() {
auto t = std::make_tuple(1, 2, 2.0, 3.0f);
auto t2 = std::make_tuple(1, 2);
f( t2 , t ); // FIXED
f2(t, 1, 2); // Ok, no problem
f3(t2, t);
}
问题
template<typename... Args, typename... TupleArgs>
void f( Args&&... , const std::tuple<TupleArgs...>& );
是Args&&...
是不可推导的,因为不是最后一个参数。
可能的解决方法是提供模板:
f<int, int> ( 1 , 2 , t );
(那么Args&&...
大概应该是Args...
)。
另一种解决方法是将它放在最后一个参数中,并使用 SFINAE 只允许元组作为最后一个参数:
// Traits to detect std::tuple
template <typename> struct is_tuple : std::false_type{};
template <typename...Ts> struct is_tuple<std::tuple<Ts...>> : std::true_type{};
// Helper function to retrieve tuple and argument
template <typename Tuple, std::size_t...Is>
void f3_impl(Tuple&& tuple, std::index_sequence<Is...>)
{
f2(std::get<sizeof...(Is)>(std::forward<Tuple>(tuple)),
std::get<Is>(std::forward<Tuple>(tuple))...);
}
template<typename... Args>
std::enable_if_t<sizeof...(Args) != 0
&& (is_tuple<std::decay_t<Args>>::value, ...)> /* Only use LAST value */
f3( Args&&... args)
{
f3_impl(std::forward_as_tuple(std::forward<Args>(args)...),
std::make_index_sequence<sizeof...(Args) - 1>());
}
我正在尝试元编程。当我尝试将 variadic types
与 std::tuple
连接时,我遇到了一个问题。
#include <tuple>
template<typename... Args , typename... TupleArgs>
void f( Args&&... , const std::tuple<TupleArgs...>& )
{ }
template<typename... Args , typename... TupleArgs>
void f2( const std::tuple<TupleArgs...>& , Args&&... )
{ }
int main(){
auto t = std::make_tuple( 1 , 2 , 2.0 , 3.0f );
// f ( 1 , 2 , t ); // deduction/substition failed
f2( t , 1 , 2 ); // Ok, no problem
}
我直觉上希望这两个函数都能编译无误,但编译器似乎对此有所抱怨。
有人可以解释为什么 f
会给出 deduction/substitution 错误而 f2
不会吗?
是否有解决方法(我确定有)?
编译器:x86-x64 GCC 9.2 带有 -std=c++17 选项
f
失败,因为编译器无法判断您传递给它的元组参数 t
是 Args&&...
列表的一部分还是 const std::tuple<TupleArgs...>&
实例(在本例中是一个 SINGLE 元组,恰好具有可变类型)。
f2
的第一个参数实际上是一个单一的类型(元组)后面跟着一个可变参数包Args&&...
,所以你在第一个参数之后传递的任何参数显然都属于Args&&...
.请注意,我指的是实际函数的参数,而不是模板声明。函数原型是导致此行为的原因。
如果你想在一个函数参数列表中有两个参数包,那么你可以做一些技巧,比如把两个都放在元组中,或者把一个放在模板模板参数(嵌套模板)中。
我提供了两个示例解决方案。我更改了 f
以使用两个元组,并创建了一个使用模板模板参数的新函数 f3
。
#include <tuple>
template<typename... Args, typename... TupleArgs>
void f(const std::tuple<Args...>&, const std::tuple<TupleArgs...>&)
{ }
template<typename... TArgs, template <typename...> class T, typename... TupleArgs>
void f3(T<TArgs...>&, const std::tuple<TupleArgs...>&)
{ }
template<typename... Args, typename... TupleArgs>
void f2(const std::tuple<TupleArgs...>&, Args&&...)
{ }
int main() {
auto t = std::make_tuple(1, 2, 2.0, 3.0f);
auto t2 = std::make_tuple(1, 2);
f( t2 , t ); // FIXED
f2(t, 1, 2); // Ok, no problem
f3(t2, t);
}
问题
template<typename... Args, typename... TupleArgs>
void f( Args&&... , const std::tuple<TupleArgs...>& );
是Args&&...
是不可推导的,因为不是最后一个参数。
可能的解决方法是提供模板:
f<int, int> ( 1 , 2 , t );
(那么Args&&...
大概应该是Args...
)。
另一种解决方法是将它放在最后一个参数中,并使用 SFINAE 只允许元组作为最后一个参数:
// Traits to detect std::tuple
template <typename> struct is_tuple : std::false_type{};
template <typename...Ts> struct is_tuple<std::tuple<Ts...>> : std::true_type{};
// Helper function to retrieve tuple and argument
template <typename Tuple, std::size_t...Is>
void f3_impl(Tuple&& tuple, std::index_sequence<Is...>)
{
f2(std::get<sizeof...(Is)>(std::forward<Tuple>(tuple)),
std::get<Is>(std::forward<Tuple>(tuple))...);
}
template<typename... Args>
std::enable_if_t<sizeof...(Args) != 0
&& (is_tuple<std::decay_t<Args>>::value, ...)> /* Only use LAST value */
f3( Args&&... args)
{
f3_impl(std::forward_as_tuple(std::forward<Args>(args)...),
std::make_index_sequence<sizeof...(Args) - 1>());
}