代码之家  ›  专栏  ›  技术社区  ›  Antoine Morrier

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

  •  9
  • Antoine Morrier  · 技术社区  · 8 年前

    我要转换的“数组” 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,出现以下错误: 表达式的计算结果不是常量

    2 回复  |  直到 8 年前
        1
  •  7
  •   JVApen    8 年前

    此代码看起来正确,在编译为C++17时可以正常工作。 它使用 std::array::begin ,仅已制作 constexpr 在C++17中。

    在以下情况下,可以实现更好的编译错误 using clang ,其中说明:

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

    是否有其他可能?

    关于正确性,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>>{}, "!" );
     }
    
    推荐文章