1

我正在尝试实现数字文字运算符模板。

#include <string_view>
#include <cstdint>
#include <cmath>
#include <iostream>
#include <boost/mp11/integer_sequence.hpp>
#include <boost/mp11/algorithm.hpp>
using namespace boost::mp11;

template <char... Cs>
[[nodiscard]] constexpr auto operator""_c(){
    int weight =std::pow(10, sizeof... (Cs));
    // unused, would like to transform it using lambda that mutably captures
    // weight
    using ints = index_sequence<sizeof... (Cs)>; 
    // ugly fold way
    auto val = ((weight/=10,(int)(Cs-'0')*weight) + ...);
    return val;
}


int main(){
    std::cout << 0_c  << std::endl;
    std::cout << 00_c << std::endl;
    std::cout << 01_c << std::endl;
    std::cout << 123_c << std::endl;
}

代码适用于简单的情况(正确性并不重要,例如负数),它只是一个示例,但代码看起来很难看,并且 clang 会发出多次修改权重的警告,所以我猜代码是错误的(未定义或未指定的行为)虽然它似乎工作......

现在我想知道有没有办法让我ints用有状态的 lambda(修改权重)来转换我使用的(它来自 boost::mp11,但同样的东西存在于 std::)。所以我想将整数转移<0,1,2>到类似的东西中<100,10,1>

我想这已经被问过了,但这很难搜索。

需要明确的是:运算符“”只是一个玩具问题,我真正的问题是关于用有状态的 lambda 映射整数序列的值。

另外,如果问题不清楚:我很高兴使用 boost mp11,但在文档中找不到任何内容。

4

2 回答 2

2

所以我想将 <0,1,2> 的整数转换成 <100,10,1> 之类的东西

首先,您可以转换std::index_sequencestd::array,然后像往常一样对其执行操作,最后再次转换std::arraystd::index_sequence

为了让有状态 lambda 在编译时工作,我们可以接受一个可以返回有状态 lambda 的函数,然后在内部获取它:

template<std::size_t... Is>
constexpr auto transform_seq(std::index_sequence<Is...>, auto get_op) {
  // index_sequence -> array
  constexpr auto arr = [op = get_op()]() mutable {
    std::array<std::size_t, sizeof...(Is)> arr{Is...};
    for (auto& value : arr)
      value = op(value);
    return arr;
  }();

  // array -> index_sequence
  constexpr auto seq = [&]<std::size_t... Js>(std::index_sequence<Js...>) {
    return std::index_sequence<std::get<Js>(arr)...>{};
  }(std::make_index_sequence<arr.size()>{});

  return seq;
};

然后你可以index_sequence根据op你传入的进行转换:

using input1 = std::index_sequence<0,1,2>;
auto gen_op1 = [] { 
  return [w = 1000](auto x) mutable { w /= 10; return w; }; 
};
using res1 = decltype(transform_seq(input1{}, gen_op1));
static_assert(std::same_as<res1, std::index_sequence<100, 10, 1>>);

using input2 = std::index_sequence<0,1,2,3>;
auto gen_op2 = [] { 
  return [b = true] (auto x) mutable { b = !b; return b * 10 + x; }; 
};
using res2 = decltype(transform_seq(input2{}, gen_op2));
static_assert(std::same_as<res2, std::index_sequence<0,11,2,13>>);

演示。

于 2021-10-07T17:18:57.457 回答
1

我想你想要:

template <typename F, std::size_t ... Is>
constexpr auto apply(F f, std::index_sequence<Is...>)
-> std::index_sequence<f(Is)...>
{
    return {};
}

template <char... Cs>
[[nodiscard]] constexpr auto operator""_c(){

    return []<std::size_t ... Pows>(std::index_sequence<Pows...>){
        return ((Pows * (Cs - '0')) + ...);
    }(apply([](std::size_t n){ return ipow(10, sizeof...(Cs) - n - 1);},
            std::make_index_sequence<sizeof...(Cs)>()));
}

演示

但是直接进行计算似乎更简单:

template <char... Cs>
[[nodiscard]] constexpr auto operator""_c(){
    constexpr auto res = 
        []<std::size_t ... Is>(std::index_sequence<Is...>){
            return ((ipow(10, sizeof...(Cs) - Is - 1) * (Cs - '0')) + ...);
        }(std::make_index_sequence<sizeof...(Cs)>());
    return res;
}
于 2021-10-07T15:50:05.667 回答