【问题标题】:Variadic template parameter pack deduction failure可变参数模板参数包推演失败
【发布时间】:2020-03-10 06:38:07
【问题描述】:

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

see online

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

有人可以解释为什么f 给出扣除/替换错误但f2 没有?

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

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

【问题讨论】:

  • 如果您还提及您正在使用的编译器会有所帮助。
  • @TanveerBadar 它实际上存在于链接中,但我正在添加

标签: c++ variadic-templates template-argument-deduction


【解决方案1】:

f 失败,因为编译器无法判断您传递给它的元组参数 t 是 Args&amp;&amp;... 列表的一部分还是 const std::tuple&lt;TupleArgs...&gt;&amp; 实例(在这种情况下是 SINGLE 元组恰好有一个可变参数类型)。

f2的第一个参数实际上是一个单一类型(元组),后面跟着一个可变参数包Args&amp;&amp;...,所以你在第一个参数之后传递的任何参数显然都属于Args&amp;&amp;...。请注意,我指的是实际函数的参数,而不是模板声明。函数原型是导致这种行为的原因。

如果你想在一个函数参数列表中包含两个参数包,那么你可以做一些技巧,比如将 BOTH 放入元组中,或者将一个放入模板模板参数(嵌套模板)中。

我提供了两个示例解决方案。我将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);
}

【讨论】:

    【解决方案2】:

    问题

    template<typename... Args, typename... TupleArgs>
    void f( Args&&... , const std::tuple<TupleArgs...>& );
    

    Args&amp;&amp;... 是不可演绎的,因为不是最后一个参数。

    可能的解决方法是提供模板:

    f&lt;int, int&gt; ( 1 , 2 , t );(那么Args&amp;&amp;... 应该是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

    【讨论】:

    • 对不起,你写的那一刻我不明白你的答案,但现在我明白了你的所作所为。 (is_tuple&lt;std::decay_t&lt;Args&gt;&gt;::value, ...) 是一个很好的技巧来说明最后一个类型参数是一个元组。赞成。
    猜你喜欢
    • 1970-01-01
    • 2012-10-26
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 2020-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多