如何对(任意)POD C++ 结构施加词典顺序?
How to impose a lexicographic order on an (arbitrary) POD C++ struct?
我有一些 POD struct foo
;假设它是 struct foo { int x; unsigned y; }
。我希望能够比较 struct foo
的使用词典顺序 - 当然是按其字段的顺序。也就是说,我希望所有运算符 <、==、> 等都适用于 struct foo
的
我能否以某种通用的方式做到这一点,而不用任何 reflection voodoo 装饰我的结构定义 - 并且不只是拼写出所有这些运算符定义?还是能够做到这一点 "language reflection dependent" 期望值过高?
Can I do this in some generic way, without having decorated my structure definition with any reflection voodoo - and without just spelling out all those operator definitions?
不,目前的 c++ 标准无法以通用方式实现这一点。
我什至不知道你说的 "reflection voodo" 是什么意思,因为标准还不支持类型反射。
即使将来是这样,我怀疑像 按字典顺序列出 这样的操作是否会首先可用。
Or is the ability to do this too much of a "language reflection dependent" expectation?
可能是的。您可以尝试使用像 c# 这样具有反射的语言,但提供通用运算符实现仍然很棘手。
目前没有类似
的快捷方式
auto operator < (const foo &a, const foo &b) {
return std::tie(a.x, a.y) < std::tie(b.x, b.y);
}
在标准 C++ 中(以及在 Boost afaics 中)。
由于这确实是不必要且容易出错的类型,Defaulted comparison operators 已被提议,但尚未添加到标准 C++(截至 C++17 的当前草案)。
你不能在标准 C++11 或 C++14 中这样做。
您可以考虑让一些程序或脚本生成相关的 struct
-s 及其比较函数。也许使用像 GPP or m4 (or write your own C++ generator). Qt moc
这样的外部预处理器可能会鼓舞人心。
或者您可以考虑使用一些编译器插件(如果使用 GCC: coded in C++, or in MELT; if using Clang: coded in C++)来帮助完成这项工作。这可能需要数周的工作(因为 C++ 编译器是非常复杂的野兽),因此仅对于大型程序是值得的。
您可以在 C++1z 中执行此操作。根据 的回答,我准备了以下概念证明:
struct anything {
template<class T> operator T()const;
};
namespace details {
template<class T, class Is, class=void>
struct can_construct_with_N:std::false_type {};
template<class T, std::size_t...Is>
struct can_construct_with_N<T, std::index_sequence<Is...>,
std::void_t< decltype(T{(void(Is),anything{})...}) >>:
std::true_type
{};
}
template<class T, std::size_t N>
using can_construct_with_N=details::can_construct_with_N<T, std::make_index_sequence<N>>;
namespace details {
template<std::size_t Min, std::size_t Range, template<std::size_t N>class target>
struct maximize: std::conditional_t<
maximize<Min, Range/2, target>{} == (Min+Range/2)-1,
maximize<Min+Range/2, (Range+1)/2, target>,
maximize<Min, Range/2, target>
>{};
template<std::size_t Min, template<std::size_t N>class target>
struct maximize<Min, 1, target>: std::conditional_t<
target<Min>{},
std::integral_constant<std::size_t,Min>,
std::integral_constant<std::size_t,Min-1>
>{};
template<std::size_t Min, template<std::size_t N>class target>
struct maximize<Min, 0, target>:
std::integral_constant<std::size_t,Min-1>
{};
template<class T>
struct construct_searcher {
template<std::size_t N>
using result = ::can_construct_with_N<T, N>;
};
template<class T, std::size_t Cap=4>
using construct_arity = details::maximize< 0, Cap, details::construct_searcher<T>::template result >;
template<typename T>
constexpr auto tie_as_tuple_impl(std::integral_constant<size_t, 1>, T&& t){
auto&& [a] = t;
return std::tie(a);
}
template<typename T>
constexpr auto tie_as_tuple_impl(std::integral_constant<size_t, 2>, T&& t){
auto&& [a,b] = t;
return std::tie(a,b);
}
template<typename T>
constexpr auto tie_as_tuple_impl(std::integral_constant<size_t, 3>, T&& t){
auto&& [a,b,c] = t;
return std::tie(a,b,c);
}
template<size_t S, typename T>
constexpr auto tie_as_tuple(T&& t){
return tie_as_tuple_impl(std::integral_constant<size_t, S>{}, std::forward<T>(t));
}
}
template<typename T>
constexpr auto tie_as_tuple(T&& t){
constexpr size_t S = details::construct_arity<std::decay_t<T>>::value;
return details::tie_as_tuple<S>(std::forward<T>(t));
}
现在,您可以使用 tie_as_tuple
创建一个元组,其中已按照您要求的方式定义了您要求的所有运算符。
请注意,我必须准备几个 tie_as_tuple_impl
的重载,每个重载对应一个结构中的每个元素数量,但它会随着结构元素的数量线性增长。
在 C++14 中,magic_get
that could allow similar solution, but it has its caveats, see here 提供了更多信息。
从 C++20 开始,只需将默认的 spaceship-operator 添加到 class、
struct foo
{
//...
auto operator<=>(foo const&) = default;
};
我想现在任何有兴趣的人都知道了,但它仍然可以作为问题的答案。
我有一些 POD struct foo
;假设它是 struct foo { int x; unsigned y; }
。我希望能够比较 struct foo
的使用词典顺序 - 当然是按其字段的顺序。也就是说,我希望所有运算符 <、==、> 等都适用于 struct foo
的
我能否以某种通用的方式做到这一点,而不用任何 reflection voodoo 装饰我的结构定义 - 并且不只是拼写出所有这些运算符定义?还是能够做到这一点 "language reflection dependent" 期望值过高?
Can I do this in some generic way, without having decorated my structure definition with any reflection voodoo - and without just spelling out all those operator definitions?
不,目前的 c++ 标准无法以通用方式实现这一点。
我什至不知道你说的 "reflection voodo" 是什么意思,因为标准还不支持类型反射。
即使将来是这样,我怀疑像 按字典顺序列出 这样的操作是否会首先可用。
Or is the ability to do this too much of a "language reflection dependent" expectation?
可能是的。您可以尝试使用像 c# 这样具有反射的语言,但提供通用运算符实现仍然很棘手。
目前没有类似
的快捷方式auto operator < (const foo &a, const foo &b) {
return std::tie(a.x, a.y) < std::tie(b.x, b.y);
}
在标准 C++ 中(以及在 Boost afaics 中)。
由于这确实是不必要且容易出错的类型,Defaulted comparison operators 已被提议,但尚未添加到标准 C++(截至 C++17 的当前草案)。
你不能在标准 C++11 或 C++14 中这样做。
您可以考虑让一些程序或脚本生成相关的 struct
-s 及其比较函数。也许使用像 GPP or m4 (or write your own C++ generator). Qt moc
这样的外部预处理器可能会鼓舞人心。
或者您可以考虑使用一些编译器插件(如果使用 GCC: coded in C++, or in MELT; if using Clang: coded in C++)来帮助完成这项工作。这可能需要数周的工作(因为 C++ 编译器是非常复杂的野兽),因此仅对于大型程序是值得的。
您可以在 C++1z 中执行此操作。根据
struct anything {
template<class T> operator T()const;
};
namespace details {
template<class T, class Is, class=void>
struct can_construct_with_N:std::false_type {};
template<class T, std::size_t...Is>
struct can_construct_with_N<T, std::index_sequence<Is...>,
std::void_t< decltype(T{(void(Is),anything{})...}) >>:
std::true_type
{};
}
template<class T, std::size_t N>
using can_construct_with_N=details::can_construct_with_N<T, std::make_index_sequence<N>>;
namespace details {
template<std::size_t Min, std::size_t Range, template<std::size_t N>class target>
struct maximize: std::conditional_t<
maximize<Min, Range/2, target>{} == (Min+Range/2)-1,
maximize<Min+Range/2, (Range+1)/2, target>,
maximize<Min, Range/2, target>
>{};
template<std::size_t Min, template<std::size_t N>class target>
struct maximize<Min, 1, target>: std::conditional_t<
target<Min>{},
std::integral_constant<std::size_t,Min>,
std::integral_constant<std::size_t,Min-1>
>{};
template<std::size_t Min, template<std::size_t N>class target>
struct maximize<Min, 0, target>:
std::integral_constant<std::size_t,Min-1>
{};
template<class T>
struct construct_searcher {
template<std::size_t N>
using result = ::can_construct_with_N<T, N>;
};
template<class T, std::size_t Cap=4>
using construct_arity = details::maximize< 0, Cap, details::construct_searcher<T>::template result >;
template<typename T>
constexpr auto tie_as_tuple_impl(std::integral_constant<size_t, 1>, T&& t){
auto&& [a] = t;
return std::tie(a);
}
template<typename T>
constexpr auto tie_as_tuple_impl(std::integral_constant<size_t, 2>, T&& t){
auto&& [a,b] = t;
return std::tie(a,b);
}
template<typename T>
constexpr auto tie_as_tuple_impl(std::integral_constant<size_t, 3>, T&& t){
auto&& [a,b,c] = t;
return std::tie(a,b,c);
}
template<size_t S, typename T>
constexpr auto tie_as_tuple(T&& t){
return tie_as_tuple_impl(std::integral_constant<size_t, S>{}, std::forward<T>(t));
}
}
template<typename T>
constexpr auto tie_as_tuple(T&& t){
constexpr size_t S = details::construct_arity<std::decay_t<T>>::value;
return details::tie_as_tuple<S>(std::forward<T>(t));
}
现在,您可以使用 tie_as_tuple
创建一个元组,其中已按照您要求的方式定义了您要求的所有运算符。
请注意,我必须准备几个 tie_as_tuple_impl
的重载,每个重载对应一个结构中的每个元素数量,但它会随着结构元素的数量线性增长。
在 C++14 中,magic_get
that could allow similar solution, but it has its caveats, see here 提供了更多信息。
从 C++20 开始,只需将默认的 spaceship-operator 添加到 class、
struct foo
{
//...
auto operator<=>(foo const&) = default;
};
我想现在任何有兴趣的人都知道了,但它仍然可以作为问题的答案。