0

我有一些 POD struct foo;假设它是struct foo { int x; unsigned y; }. 我希望能够struct foo使用字典顺序来比较 's - 当然是按他们的字段顺序。也就是说,我希望所有运算符 <、==、> 等都为struct foo's工作

我可以以某种通用方式做到这一点,而无需使用任何反射巫术来修饰我的结构定义- 并且不只是拼出所有这些运算符定义吗?或者,这样做的能力是否过于依赖“语言反射”的期望?

4

4 回答 4

5

您可以在 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_implstruct 中的每个元素数量准备几个重载,但它会根据 struct 元素的数量线性缩放。


在 C++14 中有magic_get可能允许类似的解决方案,但它有其警告,请参阅此处了解更多信息。

于 2016-11-12T16:39:36.103 回答
2

我可以以某种通用的方式做到这一点,而不用任何反射巫毒来修饰我的结构定义 - 并且不只是拼出所有这些运算符定义吗?

不,没有办法用当前的 c++ 标准以通用的方式实现这一点。

我什至不知道“反射巫毒”是什么意思,因为标准不支持类型反射(还)。

即使将来是这样,我也怀疑像按字典顺序排列的列表这样的操作是否会首先可用。


或者,这样做的能力是否过于依赖“语言反射”的期望?

大概是。您可以尝试使用像 c# 这样具有反射的语言,但提供通用运算符实现仍然很棘手。

于 2016-11-12T16:31:16.587 回答
1

目前没有类似的捷径

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 的当前草案)。

于 2016-11-12T16:35:32.447 回答
0

你不能在标准 C++11 或 C++14 中做到这一点。

您可以考虑让一些程序或脚本同时生成相关的struct-s 及其比较功能。也许使用一些外部预处理器,如GPPm4(或编写您自己的 C++ 生成器)。Qtmoc可能是鼓舞人心的。

或者您可能会考虑使用一些编译器插件(如果使用GCC用 C++MELT编码;如果使用Clang用 C++编码)来帮助完成这项工作。这可能需要几个星期的工作(因为 C++ 编译器是非常复杂的野兽),所以只对大型程序有价值。

于 2016-11-12T16:36:36.087 回答