Variadic 模板参数包推导失败

Variadic template parameter pack deduction failure

我正在尝试元编程。当我尝试将 variadic typesstd::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
} 

see online

我直觉上希望这两个函数都能编译无误,但编译器似乎对此有所抱怨。

有人可以解释为什么 f 会给出 deduction/substitution 错误而 f2 不会吗?

是否有解决方法(我确定有)?

编译器:x86-x64 GCC 9.2 带有 -std=c++17 选项

f 失败,因为编译器无法判断您传递给它的元组参数 tArgs&&... 列表的一部分还是 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>());
}

Demo