如何使用访问者对一系列变体求和?
How to sum over an array of variant using a visitor?
我正在尝试找到一种方法来使用访问者对 std::variant
的 std::array
求和。我已经走到这一步了,但我一辈子都想不出如何在不在访问者 lambda 列表的头部包含 void
条目的情况下推断出访问者的类型。
有谁知道我可以推断出访问者中 lambda 的 return 类型的方法,这样我就不必依赖它了吗?
这是我现在得到的:
#include <array>
#include <iostream>
#include <string_view>
#include <type_traits>
#include <variant>
using namespace std::literals::string_view_literals;
template<typename... Base>
struct Visitor: Base ... {
using Base::operator()...;
};
template<typename... T>
Visitor(T...) -> Visitor<T...>;
// There has to be a better way to deduce Result than what I'm doing...
template<typename... T, typename S, typename... Ss, size_t N, typename Result = typename std::result_of_t<S()>>
constexpr std::enable_if_t<std::is_arithmetic_v<Result>, Result>
summation(const Visitor<S, Ss...> &visitor, const std::array<std::variant<T...>, N> &array) {
Result sum{};
for (const auto &a: array)
sum += std::visit(visitor, a);
return sum;
}
int main() {
constexpr Visitor visitor {
// This first entry should be unnecessary, I would think:
[]() -> double { return 0; },
[](double d) -> double { return d + 3.4; },
[](int i) -> double { return i - 2; },
[](std::string_view s) -> double { return s.size(); }
};
constexpr std::array<std::variant<int, double, std::string_view>, 5> arr{9.0, 9, 3, 5.2, "hello world"sv};
constexpr auto val = summation(visitor, arr);
std::cout << val << '\n';
}
编辑:我希望结果为 constexpr
。
感谢您的帮助。
当 auto
让编译器为您处理时,您的类型推断过于明确。
一旦你在函数的范围内 decltype()
和 std::declval()
就可以轻松地进行推理(需要创建默认初始化的目标),因为你可以简单地模拟实际调用访客。
template<typename... T, typename S, typename... Ss, size_t N>
constexpr auto summation(const Visitor<S, Ss...> &visitor, const std::array<std::variant<T...>, N> &array) {
using Result = decltype(std::visit(visitor, std::declval<std::variant<T...>>()));
static_assert(std::is_arithmetic_v<Result>);
Result sum{};
for (const auto &a: array)
sum += std::visit(visitor, a);
return sum;
}
我实际上更喜欢这种风格,因为错误的调用实际上会产生一个合理的错误消息,而不是 "function not found"。那是除非你有非算术版本的 accumulate()
你正试图 sfinae 反对(这会很奇怪)。
Frank 的 decltype()
/std::declval()
解决方案的简化(我希望)。
使用decltype()
/std::declval()
,你不需要知道S
、Ss...
和T...
;您只需要 visitor
的模板类型 V
和 array
.
的模板类型
您也可以避免 static_assert()
,如果您愿意,只需编写
即可重新启用 SFINAE
template <typename V, typename A,
typename R = decltype(std::visit(std::declval<V>(), std::declval<A>().at(0)))>
constexpr std::enable_if_t<std::is_arithmetic_v<R>, R>
summation(V const & visitor, A const &array)
{
R sum{};
for (const auto &a: array)
sum += std::visit(visitor, a);
return sum;
}
我正在尝试找到一种方法来使用访问者对 std::variant
的 std::array
求和。我已经走到这一步了,但我一辈子都想不出如何在不在访问者 lambda 列表的头部包含 void
条目的情况下推断出访问者的类型。
有谁知道我可以推断出访问者中 lambda 的 return 类型的方法,这样我就不必依赖它了吗?
这是我现在得到的:
#include <array>
#include <iostream>
#include <string_view>
#include <type_traits>
#include <variant>
using namespace std::literals::string_view_literals;
template<typename... Base>
struct Visitor: Base ... {
using Base::operator()...;
};
template<typename... T>
Visitor(T...) -> Visitor<T...>;
// There has to be a better way to deduce Result than what I'm doing...
template<typename... T, typename S, typename... Ss, size_t N, typename Result = typename std::result_of_t<S()>>
constexpr std::enable_if_t<std::is_arithmetic_v<Result>, Result>
summation(const Visitor<S, Ss...> &visitor, const std::array<std::variant<T...>, N> &array) {
Result sum{};
for (const auto &a: array)
sum += std::visit(visitor, a);
return sum;
}
int main() {
constexpr Visitor visitor {
// This first entry should be unnecessary, I would think:
[]() -> double { return 0; },
[](double d) -> double { return d + 3.4; },
[](int i) -> double { return i - 2; },
[](std::string_view s) -> double { return s.size(); }
};
constexpr std::array<std::variant<int, double, std::string_view>, 5> arr{9.0, 9, 3, 5.2, "hello world"sv};
constexpr auto val = summation(visitor, arr);
std::cout << val << '\n';
}
编辑:我希望结果为 constexpr
。
感谢您的帮助。
当 auto
让编译器为您处理时,您的类型推断过于明确。
一旦你在函数的范围内 decltype()
和 std::declval()
就可以轻松地进行推理(需要创建默认初始化的目标),因为你可以简单地模拟实际调用访客。
template<typename... T, typename S, typename... Ss, size_t N>
constexpr auto summation(const Visitor<S, Ss...> &visitor, const std::array<std::variant<T...>, N> &array) {
using Result = decltype(std::visit(visitor, std::declval<std::variant<T...>>()));
static_assert(std::is_arithmetic_v<Result>);
Result sum{};
for (const auto &a: array)
sum += std::visit(visitor, a);
return sum;
}
我实际上更喜欢这种风格,因为错误的调用实际上会产生一个合理的错误消息,而不是 "function not found"。那是除非你有非算术版本的 accumulate()
你正试图 sfinae 反对(这会很奇怪)。
Frank 的 decltype()
/std::declval()
解决方案的简化(我希望)。
使用decltype()
/std::declval()
,你不需要知道S
、Ss...
和T...
;您只需要 visitor
的模板类型 V
和 array
.
您也可以避免 static_assert()
,如果您愿意,只需编写
template <typename V, typename A,
typename R = decltype(std::visit(std::declval<V>(), std::declval<A>().at(0)))>
constexpr std::enable_if_t<std::is_arithmetic_v<R>, R>
summation(V const & visitor, A const &array)
{
R sum{};
for (const auto &a: array)
sum += std::visit(visitor, a);
return sum;
}