我有以下情況:我必須將幾個指標和一個識別符號打包到一個像這樣的元組中:
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);
}
當我們直接比較兩個元組時,這種結構非常有效。現在,我想lesser()在std::sort(). 但是,g 和 clang 都抱怨他們不能“無法推斷模板引數 '_Compare'”。換句話說,我們需要將正確的模板引數傳遞給 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 函式。
謝謝!
更新:
好的,我發現了一個可行的小技巧:
sort(all.begin(), all.end(),
[](const auto &a, const auto &b) {
return lesser(a, b);
}
);
基本上,我們將它包裝成一個 lambda,因此編譯器可以推斷出型別。但是,我們能做得更好嗎?
謝謝
uj5u.com熱心網友回復:
正如評論中所建議的,您可以將比較器添加到函式物件中并將物件的實體傳遞給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;
}
uj5u.com熱心網友回復:
我認為我們可以使用它。當然,我不知道你的元組可以更復雜。
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);
}
}
};
uj5u.com熱心網友回復:
通過執行非“遞回”函式,您可以執行“單線”:
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。
沒有它,至少需要一個輔助函式,因此與其他解決方案一樣,它將函式包裝在函子中。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/515152.html
標籤:C 模板标准元组
