C++ constexpr:在编译时计算标准数组

C++ constexpr : Compute a std array at compile time

本文关键字:计算 标准 数组 编译 constexpr C++      更新时间:2023-10-16

我想将bool的"数组"转换为整数序列。所以我需要在编译时计算std::array

这是我的代码

#include <array>
template<typename InputIt, typename T >
inline constexpr typename std::iterator_traits<InputIt>::difference_type
count( InputIt first, InputIt last, const T &value ) {
    typename std::iterator_traits<InputIt>::difference_type ret = 0;
        for (; first != last; ++first) {
            if (*first == value) {
                ret++;
            }
        }
        return ret;
}
template<bool ..._values>
struct keep_value {
    static constexpr std::size_t numberOfValues = sizeof...(_values);
    static constexpr bool values[] = {_values...};
    static constexpr std::size_t numberToKeep = count(values, values + numberOfValues, true);
    static constexpr std::array<std::size_t, numberToKeep> computeIndices() {
        std::array<std::size_t, numberToKeep> array{};
        auto it = array.begin();
        for(std::size_t i{0}; i < numberOfValues; ++i)
            if(values[i] == true)
                *it++ = i;
        return array;
    }
    static constexpr std::array<std::size_t, numberToKeep> indices = computeIndices();
    template<typename Indices = std::make_index_sequence<numberToKeep>>
    struct as_index_sequence{};
    template<std::size_t ...Is>
    struct as_index_sequence<std::index_sequence<Is...>> : std::index_sequence<indices[Is]...>{};
};
int main() {
    keep_value<false, true, true>::template as_index_sequence<>{}; // Should return the sequence 1 2
}

我收到调用computeIndices函数的行的错误。这段代码 c++14 是否正确?有没有可能不这样做?我正在使用MSVC,但收到此错误:表达式的计算结果不是常量

此代码看起来正确,并且在编译为 C++17 时有效。它使用std::array::begin,这在C++17中才被constexpr

使用 clang 时可以实现更好的编译错误,它指出:

<source>:23:25: note: non-constexpr function 'begin' cannot be used in a constant expression
    auto it = array.begin();

有没有可能不这样做?

关于正确性回答 JVApen(+1(。

一种可能的替代方法是完全避免std::array并使用模板专用化以递归方式构造索引序列

下面是一个完整的可编译示例

#include <utility>
#include <type_traits>
template <typename, std::size_t, bool...>
struct bar;
// true case
template <std::size_t ... Is, std::size_t I, bool ... Bs>
struct bar<std::index_sequence<Is...>, I, true, Bs...>
   : public bar<std::index_sequence<Is..., I>, I+1U, Bs...>
 { };
// false case
template <std::size_t ... Is, std::size_t I, bool ... Bs>
struct bar<std::index_sequence<Is...>, I, false, Bs...>
   : public bar<std::index_sequence<Is...>, I+1U, Bs...>
 { };
// end case
template <typename T, std::size_t I>
struct bar<T, I>
 { using type = T; };
template <bool ... Bs>
struct foo : public bar<std::index_sequence<>, 0U, Bs...>
 { };
int main()
 {
   static_assert( std::is_same<typename foo<false, true, true>::type,
                               std::index_sequence<1U, 2U>>{}, "!" );
 }

如果您不喜欢递归解决方案,我建议(只是为了好玩(另一种基于std::tuple_cat的解决方案

#include <tuple>
#include <utility>
#include <type_traits>
template <std::size_t, bool>
struct baz
 { using type = std::tuple<>; };
template <std::size_t I>
struct baz<I, true>
 { using type = std::tuple<std::integral_constant<std::size_t, I>>; };
template <std::size_t I, bool B>
using baz_t = typename baz<I, B>::type;
template <typename, bool...>
struct bar;
template <std::size_t ... Is, bool ... Bs>
struct bar<std::index_sequence<Is...>, Bs...>
 {
   template <std::size_t ... Js>
   constexpr static std::index_sequence<Js...>
      func (std::tuple<std::integral_constant<std::size_t, Js>...> const &);
   using type = decltype(func(std::tuple_cat(baz_t<Is, Bs>{}...)));
 };

template <bool ... Bs>
struct foo : public bar<std::make_index_sequence<sizeof...(Bs)>, Bs...>
 { };
int main()
 {
   static_assert( std::is_same<typename foo<false, true, true>::type,
                               std::index_sequence<1U, 2U>>{}, "!" );
 }