首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >创建integral_constants巴黎元组的通用实用程序

创建integral_constants巴黎元组的通用实用程序
EN

Stack Overflow用户
提问于 2017-04-09 22:16:07
回答 3查看 541关注 0票数 8

利用str_const,我有一个constexpr字符串。

代码语言:javascript
复制
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开始:

代码语言:javascript
复制
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);
}

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

在这里,我手动构建这个元组:

代码语言:javascript
复制
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, "");
}

问题:

现在,我想将其推广到任何具有任意数目插入符的输入字符串。

代码语言:javascript
复制
template<size_t... Ns>
using PosTuple = std::tuple<std::integral_constant<int, Ns>...>;

如何使用Ns...或其他方法生成这里所需的caretPos序列?

工作实例

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-04-10 13:40:19

有趣的问题。

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

首先,使用arrayConverter()std::array<char, N>中转换char const * (在以下代码中为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>>

密码

代码语言:javascript
复制
#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,
                 "!");
 }
票数 1
EN

Stack Overflow用户

发布于 2017-04-10 01:53:30

下面是使用一个例子的Boost.Hana (广告是C++14)。它的结果是一个元组的积分常数,这是直接要求。

代码语言:javascript
复制
#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。这意味着当使用参数比较一个字符时,该字符被称为类型的一部分。这与string不同,后者在constexpr上完全可用,不能将参数提升到函数中的常量表达式。

票数 1
EN

Stack Overflow用户

发布于 2017-04-10 01:57:27

下面是带有Scott字符串的我尽我所能

代码语言:javascript
复制
#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),但是真的很复杂

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43312515

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档