[英]Custom tuple comparator for std::sort()
我有以下情況:我必須像這樣將幾個指針和一個標識符打包到一個元組中:
typedef tuple<unsigned*, unsigned*, unsigned*, unsigned> tuple_with_pointers_t;
在這里,我有三個指針和一個 id。 在其他情況下,我可能有更多或更少的指針,但最后一個將是 id。 請注意,我僅將unsigned*
用作示例。 它可能是更復雜的對象。
現在,我想比較兩個這樣的元組的值。 即,我需要取消引用除最后一個之外的所有元組元素。 我們可以使用以下方法(在 C++17 中)將其存檔:
template <size_t I = 0, typename T, typename... Ts>
constexpr bool lesser(std::tuple<T, Ts...> a, std::tuple<T, Ts...> b)
{
if constexpr (I < sizeof...(Ts))
return (*std::get<I>(a) < *std::get<I>(b)) ||
((*std::get<I>(a) == *std::get<I>(b)) && lesser<I + 1>(a, b));
else
return std::get<I>(a) < std::get<I>(b);
}
當我們直接比較兩個元組時,這種結構工作得很好。 現在,我想在std::sort()
上使用lesser()
作為仿函數。 但是,g++ 和 clang++ 都抱怨他們不能“無法推斷模板參數‘_Compare’”。 換句話說,我們需要將正確的模板 arguments 傳遞給 lesser。
我在這里嘗試了一些東西,但沒有成功:我們有三個模板參數,我不確定如何在這里使用元組中的_Elements
。 最好的策略是什么?
這是一些玩具代碼:
#include <algorithm>
#include <iostream>
#include <tuple>
#include <vector>
using namespace std;
// My weird tuple with pointers and one unsigned index.
typedef tuple<unsigned*, unsigned*, unsigned*, unsigned> tuple_with_pointers_t;
// This works fine for two tuples directly. Note that we cannot dereference
// the last tuple element, so we compare it directly.
template <size_t I = 0, typename T, typename... Ts>
constexpr bool lesser(std::tuple<T, Ts...> a, std::tuple<T, Ts...> b)
{
if constexpr (I < sizeof...(Ts))
return (*std::get<I>(a) < *std::get<I>(b)) ||
((*std::get<I>(a) == *std::get<I>(b)) && lesser<I + 1>(a, b));
else
return std::get<I>(a) < std::get<I>(b);
}
int main() {
// Three sets of values.
vector<unsigned> values1 {1, 2, 3};
vector<unsigned> values2 {10, 20, 30};
vector<unsigned> values3 {11, 22, 33};
// Here, we pack it all together with the index.
vector<tuple_with_pointers_t> all;
for(unsigned i = 0; i < values1.size(); ++i)
all.emplace_back(&values1[i], &values2[i], &values3[i], i);
// So, it works if we want to compare two elements of our vector.
cout << "\n- t0 < t1: " << std::boolalpha << lesser(all[0], all[1]);
cout << "\n- t2 < t1: " << std::boolalpha << lesser(all[2], all[1]);
// Now, I want to sort the tuples by their values. The compiler doesn't
// like it: it cannot deduce the template parameters.
sort(all.begin(), all.end(), lesser);
return 0;
}
我感謝任何幫助,無論是使用 C++17 還是 C++20。 但我正在尋找最緊湊和優雅的方式來做到這一點。 如果可能的話,它也可以直接在sort()
調用上使用 lambda function。
謝謝!
更新:
好的,我發現了一個有用的小技巧:
sort(all.begin(), all.end(),
[](const auto &a, const auto &b) {
return lesser(a, b);
}
);
基本上,我們將其包裝成 lambda,因此編譯器可以推斷出類型。 但是,我們可以做得更好嗎?
謝謝
正如評論中所建議的,您可以將比較器添加到 function object 並將 object 的實例傳遞給sort
:
#include <algorithm>
#include <iostream>
#include <tuple>
#include <vector>
using namespace std;
// My weird tuple with pointers and one unsigned index.
typedef tuple<unsigned*, unsigned*, unsigned*, unsigned> tuple_with_pointers_t;
namespace details {
template <size_t I = 0, typename T, typename... Ts>
constexpr bool lesser(std::tuple<T, Ts...> const& a, std::tuple<T, Ts...> const& b)
{
if constexpr (I < sizeof...(Ts))
return (*std::get<I>(a) < *std::get<I>(b)) ||
((*std::get<I>(a) == *std::get<I>(b)) && lesser<I + 1>(a, b));
else
return std::get<I>(a) < std::get<I>(b);
}
}
struct Less
{
template <typename... Ts>
constexpr bool operator()(std::tuple<Ts...> const& a, std::tuple<Ts...> const& b)
{
return details::lesser<0, Ts...>(a, b);
}
};
int main() {
// Three sets of values.
vector<unsigned> values1 {1, 2, 3};
vector<unsigned> values2 {10, 20, 30};
vector<unsigned> values3 {11, 22, 33};
// Here, we pack it all together with the index.
vector<tuple_with_pointers_t> all;
for(unsigned i = 0; i < values1.size(); ++i)
all.emplace_back(&values1[i], &values2[i], &values3[i], i);
// So, it works if we want to compare two elements of our vector.
cout << "\n- t0 < t1: " << std::boolalpha << Less()(all[0], all[1]);
cout << "\n- t2 < t1: " << std::boolalpha << Less()(all[2], all[1]);
// Now, I want to sort the tuples by their values. The compiler doesn't
// like it: it cannot deduce the template parameters.
sort(all.begin(), all.end(), Less());
return 0;
}
作為替代方案,您可以將unsigned*
包裝在自定義指針類型中並為其提供比較器。 然后您可以使用元組的默認比較器,它按字典順序比較元素。
我個人更喜歡這種方法,因為代碼更具可讀性。 我不知道這是否會破壞您現有的代碼或需要進行大量重構。
#include <algorithm>
#include <iostream>
#include <tuple>
#include <vector>
using namespace std;
class Ptr
{
public:
Ptr(unsigned& v) : m_ptr(&v) {}
unsigned operator*() const {
return *m_ptr;
}
private:
unsigned* m_ptr;
};
bool operator<(Ptr const& l, Ptr const& r)
{
return *l < *r;
}
// My weird tuple with pointers and one unsigned index.
typedef tuple<Ptr, Ptr, Ptr, unsigned> tuple_with_pointers_t;
int main() {
// Three sets of values.
vector<unsigned> values1 {1, 2, 3};
vector<unsigned> values2 {10, 20, 30};
vector<unsigned> values3 {11, 22, 33};
// Here, we pack it all together with the index.
vector<tuple_with_pointers_t> all;
for(unsigned i = 0; i < values1.size(); ++i)
all.emplace_back(values1[i], values2[i], values3[i], i);
// So, it works if we want to compare two elements of our vector.
cout << "\n- t0 < t1: " << std::boolalpha << (all[0] < all[1]);
cout << "\n- t2 < t1: " << std::boolalpha << (all[2] < all[1]);
sort(all.begin(), all.end());
return 0;
}
我想我們可以用這個。 當然,我不知道你的元組可以更復雜。
template<typename T, size_t I = 0>
using type_tuple = typename std::tuple_element<I,T>::type;
template<size_t I = 0, template<typename> class F = std::less_equal>
struct TupleCompare
{
template<typename T>
bool operator()(T const &t1, T const &t2){
using _type = typename std::conditional<std::is_pointer<type_tuple<T>>::value,
typename std::remove_pointer<type_tuple<T,I>>::type, type_tuple<T>>::type;
if constexpr (I == std::tuple_size_v<T> - 1) {
return F<_type>()(std::get<I>(t1), std::get<I>(t2));
} else {
return F<_type>()(*std::get<I>(t1), *std::get<I>(t2)) && TupleCompare<I+1, F>()(t1, t2);
}
}
};
通過執行非“遞歸”function,您可以執行“單行”:
sort(all.begin(), all.end(),
[]<typename T>(const T& lhs, const T& rhs) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>){
return std::tie(std::get<Is>(lhs)...)
< std::tie(std::get<Is>(rhs)...);
}(std::make_index_sequence<std::tuple_size_v<T> - 1>{});
});
模板 lambda 是 C++20。
如果沒有它,至少需要一個助手 function,因此它就像其他解決方案一樣,將 function 包裝在一個仿函數中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.