英文:
How to make a function which works with array of pointers as well as with array of structs?
问题
我有一个用于对输入向量进行排序的函数:
std::vector<Item> SimpleSort(std::vector<Item>&& items) {
std::sort(items.begin(), items.end(),
[](const auto& left, const auto& right) { return left.id > right.id; }
);
return std::move(items);
}
另外,我有一个定义为以下的第二个向量:
std::vector<const Item*> items;
我的目标是创建一个可以处理这两种类型向量的函数。我认为可以这样做:
template<typename ItemType>
std::vector<ItemType> SimpleSort(std::vector<ItemType>&& items) {
if(std::is_pointer_v<ItemType>) {
std::sort(items.begin(), items.end(),
[](const auto left, const auto right) { return left->id > right->id; });
} else {
std::sort(items.begin(), items.end(),
[](const auto& left, const auto& right) { return left.id > right.id; });
}
return std::move(items);
}
但这并不是我想要的,因为我不想复制逻辑,因此覆盖该函数也不太合适。有没有比我提供的更好的方法?
英文:
I have got a function which sorts incoming vector
std::vector<Item> SimpleSort(std::vector<Item>&& items) {
std::sort(items.begin(), items.end(),
[](const auto& left,const auto& right) { return left.id > right.id; }
);
return std::move(items);
}
Also, I have got a second vector defined as:
std::vector<const Item*> items;
My goal is to make some function that can works with both types of vector.
I think I can do something like this:
template<typename ItemType>
std::vector<ItemType> SimpleSort(std::vector<ItemType>&& items) {
if(std::is_pointer_v<ItemType>) {
std::sort(items.begin(), items.end(),
[](const auto left,const auto right) { return left->id > right->id; });
} else {
std::sort(items.begin(), items.end(),
[](const auto& left,const auto& right) { return left.id > right.id; });
}
return std::move(items);
}
It is not that I want, because I don't want to copy logic, so overriding of that function is not suitable too. Is there any way to make it better than I offered?
答案1
得分: 3
只需将您的排序函数提取为一个命名的lambda(或一个函数,如果您愿意):
template<typename ItemType>
std::vector<ItemType> SimpleSort(std::vector<ItemType>&& items) {
constexpr auto sorter = [](const auto& left, const auto& right) { return left.id > right.id; };
if constexpr (std::is_pointer_v<ItemType>) { //请注意这里的constexpr
std::sort(items.begin(), items.end(),
[sorter](const auto& left, const auto& right) { return sorter(*left, *right); });
} else {
std::sort(items.begin(), items.end(), sorter);
}
return std::move(items);
}
if constexpr
必须使用,否则除非 Item
重载了一元运算符 operator*
,否则不会编译。
英文:
Just extract your sorting function to a named lambda (or a function if you will):
template<typename ItemType>
std::vector<ItemType> SimpleSort(std::vector<ItemType>&& items) {
constexpr auto sorter = [](const auto& left,const auto& right) { return left.id > right.id; };
if constexpr (std::is_pointer_v<ItemType>) { //note the constexpr here
std::sort(items.begin(), items.end(),
[sorter](const auto& left,const auto& right) { return sorter(*left, *right); });
} else {
std::sort(items.begin(), items.end(), sorter);
}
return std::move(items);
}
if constexpr
must be used, otherwise it will not compile unless Item
has unary operator*
overloaded.
答案2
得分: 1
这将是使用 std::invoke()
与数据成员指针的一个非常好的用例。
std::invoke
将自动处理解封装对象参数(可以传递引用、指针或 std::reference_wrapper<T>
,它会自动执行正确的操作)
示例: godbolt
template<class ItemType>
void SimpleSort(std::vector<ItemType>& items) {
using Type = std::decay_t<std::remove_pointer_t<std::unwrap_ref_decay_t<ItemType>>>;
std::sort(
items.begin(),
items.end(),
[](auto const& lhs, auto const& rhs) {
return
std::invoke(&Type::id, lhs) >
std::invoke(&Type::id, rhs);
}
);
}
// (1) 可以对项目的向量进行排序
std::vector<Item> items = /*...*/;
SimpleSort(items);
// (2) 可以对指向项目的指针的向量进行排序
std::vector<Item*> items = /*...*/;
SimpleSort(items);
// (3) 可以对项目的引用包装的向量进行排序
std::vector<std::reference_wrapper<Item>> items = /*...*/;
SimpleSort(items);
一个更简单的选项是使用新的 std::ranges::sort()
而不是 std::sort
。
范围变体允许您将投影作为第三个参数传递,该投影将使用 std::invoke()
调用。 (然后将其结果与作为第二个参数传递的比较器进行比较)
因此,对项目/指针/引用包装的向量进行排序如下:
示例: godbolt
// (1) 对项目的向量进行排序
std::vector<Item> items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &Item::id);
// (2) 对指向项目的指针的向量进行排序
std::vector<Item*> items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &Item::id);
// (3) 对项目的引用包装的向量进行排序
std::vector<std::reference_wrapper<Item>> items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &Item::id);
英文:
This would be a really good use-case for std::invoke()
with a pointer to data member.
std::invoke
will automatically take care of unwrapping the object parameter (you can pass it a reference, a pointer, or a std::reference_wrapper<T>
and it'll automatically do the right thing)
Example: godbolt
template<class ItemType>
void SimpleSort(std::vector<ItemType>& items) {
using Type = std::decay_t<std::remove_pointer_t<std::unwrap_ref_decay_t<ItemType>>>;
std::sort(
items.begin(),
items.end(),
[](auto const& lhs, auto const& rhs) {
return
std::invoke(&Type::id, lhs) >
std::invoke(&Type::id, rhs);
}
);
}
// (1) can sort a vector of items
std::vector<Item> items = /*...*/;
SimpleSort(items);
// (2) can sort a vector of pointers to items
std::vector<Item*> items = /*...*/;
SimpleSort(items);
// (3) can sort a vector of reference_wrappers to items
std::vector<std::reference_wrapper<Item>> items = /*...*/;
SimpleSort(items);
An even easier option would be to use the new std::ranges::sort()
instead of std::sort
.
The range variant allows you to pass a projection as the third parameter, which will be invoked with std::invoke()
. (the result of this will then be compared with the comparator passed as the second argument)
So sorting a vector of items / pointers / reference wrappers is as easy as:
Example: godbolt
// (1) sort a vector of items
std::vector<Item> items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &Item::id);
// (2) sort a vector of pointers to items
std::vector<Item*> items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &Item::id);
// (3) sort a vector of reference_wrappers to items
std::vector<std::reference_wrapper<Item>> items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &Item::id);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论