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

Hen*_*nke 12 c++ templates template-meta-programming variadic-templates c++11

我写了一个类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];

    //...
};
Run Code Online (Sandbox Code Playgroud)

我设法获得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, "!");
}
Run Code Online (Sandbox Code Playgroud)

生活在Wandbox(C++ 14)Live on Wandbox(C++ 11)

lll*_*lll 8

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

index = (index*a) + b
Run Code Online (Sandbox Code Playgroud)

在您自己的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...);
}
Run Code Online (Sandbox Code Playgroud)

我们使用mypair而不是std::pair因为std::pairC++ 11中的构造函数不是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];
    }
Run Code Online (Sandbox Code Playgroud)

现场演示