[英]How to impose a lexicographic order on an (arbitrary) POD C++ struct?
I have some POD struct foo
;我有一些 POD
struct foo
; suppose it's struct foo { int x; unsigned y; }
假设它是
struct foo { int x; unsigned y; }
struct foo { int x; unsigned y; }
struct foo { int x; unsigned y; }
. struct foo { int x; unsigned y; }
。 I want to be able to compare struct foo
's using lexicographic order - by order of their fields of course.我希望能够使用字典顺序来比较
struct foo
的 - 当然是按它们的字段顺序。 That is, I want all of the operators <, ==, >, etc. to work for struct foo
's也就是说,我希望所有运算符 <、==、> 等都适用于
struct foo
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?我可以以某种通用方式做到这一点,而无需使用任何反射巫术来修饰我的结构定义 - 并且不只是拼出所有这些运算符定义吗? Or is the ability to do this too much of a "language reflection dependent" expectation?
或者,这样做的能力是否过于依赖“语言反射”的期望?
You can do this in C++1z.您可以在 C++1z 中执行此操作。 Basing on this answer, I prepared the following proof of concept:
基于这个答案,我准备了以下概念证明:
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));
}
Now, you can use tie_as_tuple
to create a tuple that has all the operators you asked for already defined, in the way you asked for.现在,您可以使用
tie_as_tuple
创建一个元组,该元组已按照您要求的方式定义了您要求的所有运算符。
Note that I had to prepare several overloads of tie_as_tuple_impl
, one for each number of elements in a struct, but that scales linearly for the number of struct elements.请注意,我必须准备几个重载的
tie_as_tuple_impl
,每个重载一个用于结构中的每个元素数量,但它会随着结构元素的数量线性缩放。
In C++14 there's magic_get
that could allow similar solution, but it has its caveats, see here for more information.在 C++14 中,
magic_get
可以允许类似的解决方案,但它有一些警告,请参阅此处了解更多信息。
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?
我可以以某种通用方式做到这一点,而无需使用任何反射巫术来修饰我的结构定义 - 并且不只是拼出所有这些运算符定义吗?
No, there's no way to achieve such in a generic way with the current c++ standard.不,没有办法用当前的 c++ 标准以通用的方式实现这一点。
I don't even know what you mean with "reflection voodo" since the standard doesn't support type reflection (yet).我什至不知道“反射巫毒”是什么意思,因为标准不支持类型反射(还)。
And even if so in future, I have doubts that operations like list in lexicographical order would be available in first place.即使将来是这样,我也怀疑像按字典顺序排列的列表这样的操作是否会首先可用。
Or is the ability to do this too much of a "language reflection dependent" expectation?
或者,这样做的能力是否过于依赖“语言反射”的期望?
Probably yes.大概是。 You may try with a language like c#, wich has reflection, it'll still be tricky to provide a generic operator implementation though.
您可以尝试使用像 c# 这样具有反射的语言,但提供通用运算符实现仍然很棘手。
There is currently no shortcut for something like目前没有类似的捷径
auto operator < (const foo &a, const foo &b) {
return std::tie(a.x, a.y) < std::tie(b.x, b.y);
}
in standard C++ (and in Boost afaics).在标准 C++ 中(以及在 Boost afaics 中)。
As this is indeed needless and error-prone typing, Defaulted comparison operators have been proposed, but not yet added to standard C++ (as of the current draft for C++17).由于这确实是不必要且容易出错的类型,因此已经提出了默认比较运算符,但尚未添加到标准 C++(截至 C++17 的当前草案)。
As of C++20, it can be done simply by adding a defaulted spaceship-operator to the class,从 C++20 开始,只需在类中添加一个默认的 spaceship-operator 即可完成,
struct foo
{
//...
auto operator<=>(foo const&) = default;
};
I guess that anyone interested knows that by now, but nevertheless it might be useful as an answer to the question.我想任何有兴趣的人现在都知道这一点,但尽管如此,它可能作为问题的答案很有用。
You can't do that in standard C++11 or C++14.你不能在标准 C++11 或 C++14 中做到这一点。
You could consider having some program or script generating both the concerned struct
-s and their compare function.您可以考虑让一些程序或脚本同时生成相关的
struct
-s 及其比较函数。 Perhaps use some external preprocessor like GPP or m4 (or write your own C++ generator).也许使用一些外部预处理器,如GPP或m4 (或编写您自己的 C++ 生成器)。 Qt
moc
might be inspirational. Qt
moc
可能是鼓舞人心的。
Or you might consider having some compiler plugin (if using GCC : coded in C++ , or in MELT ; if using Clang : coded in C++ ) to help the job.或者您可能会考虑使用一些编译器插件(如果使用GCC :用 C++或MELT编码;如果使用Clang :用 C++编码)来帮助完成这项工作。 That would require perhaps several weeks of work (because C++ compilers are very complex beasts) so is worthwhile only for large programs.
这可能需要几个星期的工作(因为 C++ 编译器是非常复杂的野兽),所以只对大型程序有价值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.