簡體   English   中英

如何對(任意)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 { int x; unsigned y; } struct foo { int x; unsigned y; } 我希望能夠使用字典順序來比較struct foo的 - 當然是按它們的字段順序。 也就是說,我希望所有運算符 <、==、> 等都適用於struct foo

我可以以某種通用方式做到這一點,而無需使用任何反射巫術來修飾我的結構定義 - 並且不只是拼出所有這些運算符定義嗎? 或者,這樣做的能力是否過於依賴“語言反射”的期望?

您可以在 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可以允許類似的解決方案,但它有一些警告,請參閱此處了解更多信息。

我可以以某種通用方式做到這一點,而無需使用任何反射巫術來修飾我的結構定義 - 並且不只是拼出所有這些運算符定義嗎?

不,沒有辦法用當前的 c++ 標准以通用的方式實現這一點。

我什至不知道“反射巫毒”是什么意思,因為標准不支持類型反射(還)。

即使將來是這樣,我也懷疑像按字典順序排列的列表這樣的操作是否會首先可用。


或者,這樣做的能力是否過於依賴“語言反射”的期望?

大概是。 您可以嘗試使用像 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 中)。

由於這確實是不必要且容易出錯的類型,因此已經提出了默認比較運算符,但尚未添加到標准 C++(截至 C++17 的當前草案)。

從 C++20 開始,只需在類中添加一個默認的 spaceship-operator 即可完成,

struct foo
{
    //...

    auto operator<=>(foo const&) = default;
};

我想任何有興趣的人現在都知道這一點,但盡管如此,它可能作為問題的答案很有用。

你不能在標准 C++11 或 C++14 中做到這一點。

您可以考慮讓一些程序或腳本同時生成相關的struct -s 及其比較函數。 也許使用一些外部預處理器,如GPPm4 (或編寫您自己的 C++ 生成器)。 Qt moc可能是鼓舞人心的。

或者您可能會考慮使用一些編譯器插件(如果使用GCC用 C++MELT編碼;如果使用Clang用 C++編碼)來幫助完成這項工作。 這可能需要幾個星期的工作(因為 C++ 編譯器是非常復雜的野獸),所以只對大型程序有價值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM