c++ - 使 C++14 constexpr 函数与 C++11 兼容

标签 c++ c++11 templates variadic-templates template-meta-programming

我编写了一个类 multi_array,它是 std::array 对多维的扩展。

template <typename T, std::size_t... N>
class multi_array {
    template <std::size_t... I, typename... Idx>
    constexpr std::size_t linearized_index(meta::index_sequence<I...>,
                                           Idx... idx) const {
        std::size_t index = 0;
        using unpack = std::size_t[];
        (void)unpack{0UL,
                     ((void)(index = (index + unpack{std::size_t(idx)...}[I]) *
                                     meta::pack_element<I + 1, N...>::value),
                      0UL)...};
        return index + unpack{std::size_t(idx)...}[sizeof...(idx) - 1];
    }

    // Storage
    T m_data[meta::product<N...>::value];

    //...
};

我已经设法获得 constexpr 元素访问权限,但仅限于 C++14。问题在于函数linearized_index。它在编译时计算线性化索引。为了做到这一点,它以某种方式减少了索引的元组和维度的元组。对于这种减少,我需要函数内部的局部变量,但这在 C++11 中是不允许的。我的环境不允许使用 C++14。我可以以某种方式重写此函数以使用 C++11 吗?

我准备了一个用 C++14 编译的完整(不是那么小)示例。

#include <cstddef> // std::size_t

namespace meta {

// product

template <std::size_t...>
struct product;

template <std::size_t head, std::size_t... dim>
struct product<head, dim...> {
    static constexpr std::size_t const value = head * product<dim...>::value;
};

template <>
struct product<> {
    static constexpr std::size_t const value = 1;
};

// pack_element

template <std::size_t index, std::size_t head, std::size_t... pack>
struct pack_element {
    static_assert(index < sizeof...(pack) + 1, "index out of bounds");
    static constexpr std::size_t const value =
        pack_element<index - 1, pack...>::value;
};

template <std::size_t head, std::size_t... pack>
struct pack_element<0, head, pack...> {
    static constexpr std::size_t const value = head;
};

// index_sequence

// https://stackoverflow.com/a/24481400
template <std::size_t... I>
struct index_sequence {};

template <std::size_t N, std::size_t... I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};

template <std::size_t... I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};

} // namespace meta

template <typename T, std::size_t... N>
class multi_array {
    template <std::size_t... I, typename... Idx>
    constexpr std::size_t linearized_index(meta::index_sequence<I...>,
                                           Idx... idx) const {
        std::size_t index = 0;
        using unpack = std::size_t[];
        (void)unpack{0UL,
                     ((void)(index = (index + unpack{std::size_t(idx)...}[I]) *
                                     meta::pack_element<I + 1, N...>::value),
                      0UL)...};
        return index + unpack{std::size_t(idx)...}[sizeof...(idx) - 1];
    }

    // Storage
    T m_data[meta::product<N...>::value];

public:
    constexpr multi_array() {}

    template <typename... U>
    constexpr multi_array(U... data) : m_data{T(data)...} {}

    template <typename... Idx>
    constexpr T operator()(Idx... idx) const noexcept {
        std::size_t index = linearized_index(
            meta::make_index_sequence<sizeof...(idx) - 1>{}, idx...);
        return m_data[index];
    }
};

int main() {
    constexpr multi_array<double, 2, 2> const b = {0, 0, 0, 1};
    static_assert(b(1, 1) == 1, "!");
}

Live on Wandbox (C++14)Live on Wandbox (C++11)

最佳答案

使用 index 的关键部分是迭代循环:

index = (index*a) + b

在您自己的 C++14 解决方案中,使用了解包参数包的技巧。在 C++11 中,您可以将其表述为递归 constexpr 函数:

struct mypair {
    size_t a;
    size_t b;
};

constexpr std::size_t foo(std::size_t init) {
    return init;
}

template<class... Pair>
constexpr std::size_t foo(std::size_t init, mypair p0, Pair... ps) {
    return foo((init+p0.a)*p0.b, ps...);
}

我们使用 mypair 而不是 std::pair 因为 C++11 中 std::pair 的构造函数不是 constexpr。那么你的迭代循环可以直译为:

    template <std::size_t... I, typename... Idx>
    constexpr std::size_t linearized_index(meta::index_sequence<I...>,
                                           Idx... idx) const {
        using unpack = std::size_t[];
        return foo(0, mypair{unpack{std::size_t(idx)...}[I], meta::pack_element<I+1, N...>::value}...) + unpack{std::size_t(idx)...}[sizeof...(idx) - 1];
    }

Live Demo

关于c++ - 使 C++14 constexpr 函数与 C++11 兼容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50423413/

相关文章:

c++ - 为什么必须在哪里放置 “template”和 “typename”关键字?

c++ - 如何打开通用文件?

c++ - 我的程序的独特行为,无法识别

c++ - 什么 STL 算法可以确定容器中的一项是否满足谓词?

c++ - Lambda 捕获与参数 - 有性能差异吗?

C++11 initializer_list 构造函数,带有自定义 vector 类的头文件和 cpp 文件

c++ - 陷入模板需求循环

C++,directx 12 : Color Picking Questions

c++ - 对于具有引用返回类型的搜索算法,默认返回值应该是什么?

c++ - 为模板函数专门化模板类