简体   繁体   English

如何对(任意)POD C++ 结构施加字典顺序?

[英]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创建一个元组,该元组已按照您要求的方式定义了您要求的所有运算符。

demo演示

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).也许使用一些外部预处理器,如GPPm4 (或编写您自己的 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM