8

利用Scott Schurr 的str_const我有一个constexpr字符串。

class StrConst
{
public:
    template<size_t N>
    constexpr StrConst(const char (&str)[N])
        : str_(str)
        , len_(N - 1)
    {
        static_assert(N > 1, "not a string");
    }

    constexpr operator const char*() const
    {
        return str_;
    }

    constexpr size_t size() const
    {
        return len_;
    }

    constexpr char operator[] (size_t i) const
    {
        return i < len_ ? str_[i] : throw std::out_of_range("invalid index");
    }

private:
    const char* const str_;
    const size_t      len_;
};

我有另一个constexpr函数,它返回在字符串中找到的第一个插入符号的位置,从位置 n 开始:

constexpr int caretPos(const StrConst& str, size_t n = 0)
{
    if (n == str.size())
        return -1;

    if (str[n] == '^')
        return n;

    return caretPos(str, n+1);
}

我可以使用 of 的结果为 a ofcaretPos创建一个 typedef ,其中元组的大小是在字符串中找到的插入符号的数量,每个元组元素都是一个整数常量,其值是插入符号在字符串中的位置。std::tuplestd::integral_constants

这里我手动构造了这个元组:

int main()
{
    constexpr StrConst s("hello^world^");

    constexpr int pos1 = caretPos(s);
    constexpr int pos2 = caretPos(s, pos1+1);

    using P1 = std::integral_constant<int, pos1>;
    using P2 = std::integral_constant<int, pos2>;

    using PosTuple = std::tuple<P1, P2>;

    static_assert(std::tuple_element_t<0, PosTuple>::value == 5, "");
    static_assert(std::tuple_element_t<1, PosTuple>::value == 11, "");
}

问题:

我现在想将其推广到任何具有任意数量的脱字符号的输入字符串。

template<size_t... Ns>
using PosTuple = std::tuple<std::integral_constant<int, Ns>...>;

如何Ns...使用caretPos或其他方式生成此处所需的序列?

工作示例

4

3 回答 3

1

有趣的问题。

避免使用StrConst,在下面的示例中,您可以看到一种获取std::tuple<std::integral_constant<std::size_t, Pos1>, std::integral_constant<std::size_t, Pos2>, ...>类型的方法。

首先,在a中使用arrayConverter()转换a char const *std::array<char, N>lc在下面的代码中);第二:用于tupleIndexGenerator<lc.size(), lc, '^'>::type获取请求的类型。

所以,如果"hello^world^"是字符串,从tupleIndexGenerator<lc.size(), lc, '^'>::type你得到std::tuple<std::integral_constant<std::size_t, 5U>, std::integral_constant<std::size_t, 11U>>

编码

#include <iostream>
#include <array>
#include <tuple>

template <typename, typename>
struct typeConcat;

template <typename T0, template <typename ...> class C, typename ... Ts>
struct typeConcat<T0, C<Ts...>>
 { using type = C<T0, Ts...>; };

template <std::size_t N, std::array<char, N> const & A, char CH,
          std::size_t Pos = 0U, bool EQ = ((Pos < N) && (A.at(Pos) == CH))>
struct tupleIndexGenerator;

template <std::size_t N, std::array<char, N> const & A, char CH>
struct tupleIndexGenerator<N, A, CH, N, false>
 { using type = std::tuple<>; };

template <std::size_t N, std::array<char, N> const & A, char CH,
          std::size_t Pos>
struct tupleIndexGenerator<N, A, CH, Pos, false>
 { using type = typename tupleIndexGenerator<N, A, CH, Pos+1U>::type; };

template <std::size_t N, std::array<char, N> const & A, char CH,
          std::size_t Pos>
struct tupleIndexGenerator<N, A, CH, Pos, true>
 { using type = typename typeConcat<
      std::integral_constant<std::size_t, Pos>,
      typename tupleIndexGenerator<N, A, CH, Pos+1U>::type>::type; };

template <typename T, size_t N, size_t ... Is>
constexpr auto arrayConverter (T const (&arr)[N],
                               std::index_sequence<Is...> const &)
 { return std::array<T, N> { { arr[Is]... } }; }

template <typename T, size_t N>
constexpr auto arrayConverter (T const (&arr)[N])
 { return arrayConverter(arr, std::make_index_sequence<N>{}); }

constexpr auto lc = arrayConverter("hello^world^");

int main ()
 {
   static_assert(std::is_same<
                 typename tupleIndexGenerator<lc.size(), lc, '^'>::type,
                 std::tuple<std::integral_constant<std::size_t, 5U>,
                            std::integral_constant<std::size_t, 11U>>>::value,
                 "!");
 }
于 2017-04-10T13:40:19.037 回答
1

这是一个使用 Boost.Hana(宣传 C++14)的示例。它产生一个整数常量的元组,这正是所要求的。

#include <cstddef>
#include <utility>

#include <boost/hana.hpp>

namespace hana = boost::hana;
using namespace boost::hana::literals;

template<typename Str, int... Is>
constexpr auto unfilteredCaretsImpl(Str str, std::integer_sequence<int, Is...>) {
    return hana::make_tuple(boost::hana::if_(str[hana::int_c<Is>] == hana::char_c<'^'>, hana::just(hana::int_c<Is>), hana::nothing)...);
}

template<typename Str>
constexpr auto unfilteredCarets(Str str) {
    return unfilteredCaretsImpl(str, std::make_integer_sequence<int, hana::length(str)>{});
}

template<typename Str>
constexpr auto allCarets(Str str) {
    auto unfiltered = unfilteredCarets(str);
    auto filtered = hana::filter(unfiltered, [](auto opt) { return opt != hana::nothing; });
    return hana::transform(filtered, [](auto opt) { return opt.value(); });
}

int main() {
    constexpr auto carets = allCarets(BOOST_HANA_STRING("hello^world^"));

    static_assert(hana::length(carets) == std::size_t{2});
    static_assert(carets[0_c] == 5);
    static_assert(carets[1_c] == 11);
}

大部分工作都是为了解决每个值都是不同类型的事实。Hana 将值编码为类型的一部分,这意味着可以在常量表达式中使用参数。例如,一个 Hana 字符串是String<'a', 'b', 'c'>一些人工的类型String。这意味着当使用参数比较字符时,该字符被称为类型的一部分。这与 Scott 的字符串不同,后者在constexpr函数中完整且无法将参数提升为常量表达式。

于 2017-04-10T01:53:30.917 回答
1

这是我可以用 Scott 的字符串做的最好的事情:

#include <cstddef>
#include <stdexcept>

class StrConst
{
public:
    template<size_t N>
    constexpr StrConst(const char (&str)[N])
        : str_(str)
        , len_(N - 1)
    {
        static_assert(N > 1, "not a string");
    }

    constexpr operator const char*() const
    {
        return str_;
    }

    constexpr size_t size() const
    {
        return len_;
    }

    constexpr char operator[] (size_t i) const
    {
        return i < len_ ? str_[i] : throw std::out_of_range("invalid index");
    }

private:
    const char* const str_;
    const size_t      len_;
};

template<typename T, size_t MaxSize>
class VectorConst
{
public:
    constexpr VectorConst() = default;

    constexpr size_t size() const 
    {
        return _size; 
    }

    constexpr void push_back(const T& value) {
        _data[_size++] = value;
    }

    constexpr T& operator[](size_t i) 
    {
        return _data[i];
    }

    constexpr const T& operator[](size_t i) const
    {
        return _data[i];
    }

private:
    T _data[MaxSize]{};
    size_t _size = 0;
};

template<size_t Size>
constexpr auto allCarets(const StrConst& str) {
    VectorConst<size_t, Size> result;

    for (size_t i = 0; i < str.size(); ++i) {
        if (str[i] == '^') {
            result.push_back(i);
        }
    }

    return result;
}

int main() {
    constexpr StrConst s("hello^world^");
    constexpr auto carets = allCarets<s.size()>(s);

    static_assert(carets.size() == 2);
    static_assert(carets[0] == 5);
    static_assert(carets[1] == 11);
}

由于普通constexpr值和值没有被编码为类型,我们受到了更多的限制。我们根本无法形成整数常量的元组,因为参数中的字符串内容在常量表达式中是不可用的。相反,我制作了一个小的 constexpr 向量,我们可以像运行时一样使用它来推送我们找到的位置,但即便如此,我们也无法在编译时进行任何动态分配,因此它有一个需要的最大大小作为模板参数。使用 C++11,您也可以递归地而不是迭代地编写它,但我不确定您将如何实现push_back. 您需要复制数组并更改一个值。据我所知,您必须通过数组的初始化列表来完成它,并且基本上需要一个索引参数包,其大小基于一个不是常量表达式的变量,这是可能的(尽管我不知道 C++11),但真的很复杂

于 2017-04-10T01:57:27.333 回答