将有状态 lambda 应用于整数序列值
Apply stateful lambda to integer sequence values
我正在尝试实现数字文字运算符模板。
#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;
}
这个code适用于简单的情况(正确性并不重要,例如负数),这只是一个例子,但是代码看起来很丑,而且clang会发出多次修改权重的警告,所以我猜代码有问题(未定义或未指定的行为)虽然它似乎工作...
现在我想知道有没有办法用有状态的 lambda(修改重量)。
所以我想将 <0,1,2>
的整数转换为 <100,10,1>
我想这个问题以前有人问过,但这很难搜索。
明确一点:运算符“”只是一个玩具问题,我真正的问题是关于用有状态 lambda 映射整数序列的值。
此外,如果问题不清楚:我非常乐意使用 boost mp11,但在文档中找不到任何内容。
我想你想要:
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;
}
So I would like to transfer ints, that are <0,1,2> into something like
<100,10,1>
首先,您可以将 std::index_sequence
转换为 std::array
,然后像往常一样对其进行操作,最后,再次将 std::array
转换为 std::index_sequence
。
为了让有状态的 lambda 在编译时工作,我们可以接受一个可以 return 有状态的 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;
};
然后就可以根据你传入的op
进行index_sequence
转换:
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>>);
我正在尝试实现数字文字运算符模板。
#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;
}
这个code适用于简单的情况(正确性并不重要,例如负数),这只是一个例子,但是代码看起来很丑,而且clang会发出多次修改权重的警告,所以我猜代码有问题(未定义或未指定的行为)虽然它似乎工作...
现在我想知道有没有办法用有状态的 lambda(修改重量)。
所以我想将 <0,1,2>
的整数转换为 <100,10,1>
我想这个问题以前有人问过,但这很难搜索。
明确一点:运算符“”只是一个玩具问题,我真正的问题是关于用有状态 lambda 映射整数序列的值。
此外,如果问题不清楚:我非常乐意使用 boost mp11,但在文档中找不到任何内容。
我想你想要:
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;
}
So I would like to transfer ints, that are <0,1,2> into something like <100,10,1>
首先,您可以将 std::index_sequence
转换为 std::array
,然后像往常一样对其进行操作,最后,再次将 std::array
转换为 std::index_sequence
。
为了让有状态的 lambda 在编译时工作,我们可以接受一个可以 return 有状态的 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;
};
然后就可以根据你传入的op
进行index_sequence
转换:
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>>);