您可以使用标准库的 std::sort 算法(如果它是 constexpr,因为它应该在 C++20 中),即使它通过仅将元组的索引包装为不同的索引来期望同质类型输入std::variants 数组并对其进行排序。
然后,您可以提供所需的任何谓词,只要可以将元组中的每对类型与谓词进行比较,并且满足比较器与std::sort 的通常要求。
特别是有一个std::less<void> 专门化,它接受两种任意类型以与< 进行比较,所以我使用它作为默认值,类似于std::sort 所做的。
正如@Barry 所提到的,您需要将元组作为非类型模板参数来访问函数内部的值。如果您还想在排序算法中访问谓词的状态,那么您还需要将其作为引用非类型模板参数传递。
还请注意,由于需要比较器的实例化和访问次数,这可能会花费元组大小的二次编译时间。对于大型元组,您可能可以使用模板元编程来实现不需要二次多次实例化的自己的算法,而模板元编程只会实例化所需的比较。
或者,可以通过将元组中包含的值(或std::refs 到值)复制到std::variants 中来减少所需实例化的数量,而不是在删除重复类型后减少所需实例化的数量并在 distinct 类型的数量上以二次方的方式访问。请参阅此答案的历史,以了解实际上并未对类型进行重复数据删除的不完整实现,因此元组的大小可能仍为二次方。
namespace detail {
template<auto& t, typename Pred, auto... Is>
constexpr auto sort(Pred&& pred, std::index_sequence<Is...>) {
constexpr auto sorted_indices = [&]{
using var_t = std::variant<std::integral_constant<std::size_t, Is>...>;
std::array indices{var_t{std::in_place_index<Is>}...};
std::sort(indices.begin(), indices.end(), [&](auto x, auto y){
return std::visit([&](auto a, auto b) {
return pred(std::get<decltype(a)::value>(t), std::get<decltype(b)::value>(t));
}, x, y);
});
return indices;
}();
using old_tuple_t = std::remove_cvref_t<decltype(t)>;
using new_tuple_t = std::tuple<std::tuple_element_t<sorted_indices[Is].index(), old_tuple_t>...>;
return new_tuple_t{std::get<sorted_indices[Is].index()>(t)...};
}
}
template<auto& t, typename Pred = std::less<void>>
constexpr auto sort(Pred&& pred = {}) {
using size = std::tuple_size<std::remove_cvref_t<decltype(t)>>;
return detail::sort<t>(std::forward<Pred>(pred), std::make_index_sequence<size::value>{});
}
测试(从问题中略微更正,包括一些重复的类型):
template<typename T>
struct A{ T val; }; // a constexpr-enabled class
// Test case 1 comparing A's val members as predicate
constexpr auto t = std::make_tuple( A<int>{10}, A<float>{1.f}, A<double>{5.0}, A<int>{3});
constexpr auto s = sort<t>([](auto&& v, auto&& w){ return v.val < w.val; });
static_assert(std::is_same_v<const std::tuple<A<float>, A<int>, A<double>, A<int>>, decltype(s)>);
// Test case 2 using default `std::less<void>` for comparison
constexpr auto t2 = std::make_tuple(10, 1.f, 5.0, 3);
constexpr auto s2 = sort<t2>();
static_assert(std::is_same_v<const std::tuple<float, int, double, int>, decltype(s2)>);
在this godbolt 的 GCC 主干上测试。它不适用于带有 libstdc++ 或 libc++ 的 Clang 主干。从产生的错误消息来看,我认为这是因为它还没有实现 constexpr std::sort 。