constexpr可变参数模板并解压std :: array

马克斯

我想编写一个constexpr模板函数,以置换作为参数传入的数组元素。所以我想出了这样的东西:

template <typename T, std::size_t N, typename... Ts>
constexpr std::array<T, N> permute(const std::array<T, N>& arr, const std::array<int, N>& permutation, Ts&&... processed)
{
    return (sizeof...(Ts) == N) ?
        std::array<T, N>{ std::forward<Ts>(processed)... } :
        permute(arr, permutation, std::forward<Ts>(processed)..., arr[permutation[sizeof...(Ts)]]);
}

用法示例:

constexpr std::array<int, 3> arr{ 1, 2, 3 };
constexpr std::array<int, 3> permutation{ 2, 1, 0 };
constexpr auto result = permute(arr, permutation); //result should contain { 3, 2, 1 }

问题是上面的代码无法编译。出于某种原因,g ++ 6.4尝试使用“处理过的”模板参数包下隐藏的4个及更多参数实例化置换模板您能帮我改正代码并使之编译吗?

完整代码

托比·斯皮特(Toby Speight)

我将展示一个“快速修复”,演示问题的原因,然后展示如何在C ++ 11中解决问题。之后,我将展示如何使用较新的功能(从C ++ 14开始)以实现更简单的实现。


诊断

导致编译失控的原因是,编译器必须生成条件的两个分支并检查它们的正确性,即使它可以证明将永远不会评估其中之一。

在C ++的新版本,我们可以把它通过更换工作?if constexpr

#include <array>
#include <cstddef>
#include <utility>

template <typename T, std::size_t N, typename... Ts>
constexpr std::array<T, N> permute(const std::array<T, N>& arr,
                                   const std::array<int, N>& permutation,
                                   Ts&&... processed)
{
    if constexpr (sizeof...(Ts) == N)
        return std::array<T, N>{ std::forward<Ts>(processed)... };
    else
        return permute(arr, permutation, std::forward<Ts>(processed)...,
                       arr[permutation[sizeof...(Ts)]]);
}

int main()
{
    constexpr std::array<int, 3> arr{ 1, 2, 3 };
    constexpr std::array<int, 3> permutation{ 2, 1, 0 };
    constexpr auto result = permute(arr, permutation);

    return result != std::array<int, 3>{ 3, 2, 1 };
}

(对于这些较新版本的C ++,仍然可以使用来简化此操作std::index_sequence,我将在后面显示)。


C ++ 11代码

C ++ 11没有if constexpr,因此我们需要恢复为SFINAE:

#include <array>
#include <cstddef>
#include <utility>

template <typename T, std::size_t N, typename... Ts>
constexpr typename std::enable_if<sizeof...(Ts) == N, std::array<T, N> >::type
permute(const std::array<T, N>&, const std::array<int, N>&,
        Ts&&... processed)
{
    return std::array<T, N>{ std::forward<Ts>(processed)... };
}

template <typename T, std::size_t N, typename... Ts>
constexpr typename std::enable_if<sizeof...(Ts) != N, std::array<T, N> >::type
permute(const std::array<T, N>& arr, const std::array<int, N>& permutation,
        Ts&&... processed)
{
    return permute(arr, permutation, std::forward<Ts>(processed)...,
                   arr[permutation[sizeof...(Ts)]]);
}

在此,我们为sizeof...(Ts) == N提供了完全独立的功能sizeof...(Ts) != N,并std::enable_if可以在它们之间进行选择。


从C ++ 14开始

如果我们能够使用C ++ 14或更高版本,我们会得到std::index_sequence,这大大简化了对数组或元组的所有元素的操作。这仍然需要两个函数,但是这次它们中的一个调用另一个,并且逻辑更容易理解:

#include <array>
#include <cstddef>
#include <utility>

template<typename T, std::size_t N, std::size_t... I>
constexpr std::array<T, N>
permute_impl(const std::array<T, N>& a, const std::array<int, N>& p,
             std::index_sequence<I...>)
{
    return { a[p[I]]... };
}


template<typename T, std::size_t N, typename I = std::make_index_sequence<N>>
constexpr std::array<T, N>
permute(const std::array<T, N>& a, const std::array<int, N>& p)
{
    return permute_impl(a, p, I{});
}

index_sequence如果您不止需要一次并且只限于使用C ++ 11,甚至可能需要自己实现

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章