如何创建一个可以与指针数组和结构体数组一起使用的函数?

huangapple go评论70阅读模式
英文:

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&lt;Item&gt; SimpleSort(std::vector&lt;Item&gt;&amp;&amp; items) {
   
   std::sort(items.begin(), items.end(), 
      [](const auto&amp; left,const auto&amp; right) { return left.id &gt; right.id; }
   );

   return std::move(items); 
}

Also, I have got a second vector defined as:

std::vector&lt;const Item*&gt; 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&lt;typename ItemType&gt;
std::vector&lt;ItemType&gt; SimpleSort(std::vector&lt;ItemType&gt;&amp;&amp; items) {
   if(std::is_pointer_v&lt;ItemType&gt;) {
      std::sort(items.begin(), items.end(), 
      [](const auto left,const auto right) { return left-&gt;id &gt; right-&gt;id; });
   } else {
      std::sort(items.begin(), items.end(), 
      [](const auto&amp; left,const auto&amp; right) { return left.id &gt; 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&lt;typename ItemType&gt;
std::vector&lt;ItemType&gt; SimpleSort(std::vector&lt;ItemType&gt;&amp;&amp; items) {
   constexpr auto sorter = [](const auto&amp; left,const auto&amp; right) { return left.id &gt; right.id; };

   if constexpr (std::is_pointer_v&lt;ItemType&gt;) { //note the constexpr here
      std::sort(items.begin(), items.end(), 
      [sorter](const auto&amp; left,const auto&amp; 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&lt;T&gt;,它会自动执行正确的操作)

示例: godbolt

template&lt;class ItemType&gt;
void SimpleSort(std::vector&lt;ItemType&gt;&amp; items) {
    using Type = std::decay_t&lt;std::remove_pointer_t&lt;std::unwrap_ref_decay_t&lt;ItemType&gt;&gt;&gt;;

    std::sort(
        items.begin(),
        items.end(),
        [](auto const&amp; lhs, auto const&amp; rhs) {
            return
                std::invoke(&amp;Type::id, lhs) &gt;
                std::invoke(&amp;Type::id, rhs);
        }
    );
}

// (1) 可以对项目的向量进行排序
std::vector&lt;Item&gt; items = /*...*/;
SimpleSort(items);

// (2) 可以对指向项目的指针的向量进行排序
std::vector&lt;Item*&gt; items = /*...*/;
SimpleSort(items);

// (3) 可以对项目的引用包装的向量进行排序
std::vector&lt;std::reference_wrapper&lt;Item&gt;&gt; items = /*...*/;
SimpleSort(items);

一个更简单的选项是使用新的 std::ranges::sort() 而不是 std::sort

范围变体允许您将投影作为第三个参数传递,该投影将使用 std::invoke() 调用。 (然后将其结果与作为第二个参数传递的比较器进行比较)

因此,对项目/指针/引用包装的向量进行排序如下:

示例: godbolt

// (1) 对项目的向量进行排序
std::vector&lt;Item&gt; items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &amp;Item::id);

// (2) 对指向项目的指针的向量进行排序
std::vector&lt;Item*&gt; items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &amp;Item::id);

// (3) 对项目的引用包装的向量进行排序
std::vector&lt;std::reference_wrapper&lt;Item&gt;&gt; items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &amp;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&lt;T&gt; and it'll automatically do the right thing)

Example: godbolt

template&lt;class ItemType&gt;
void SimpleSort(std::vector&lt;ItemType&gt;&amp; items) {
    using Type = std::decay_t&lt;std::remove_pointer_t&lt;std::unwrap_ref_decay_t&lt;ItemType&gt;&gt;&gt;;

    std::sort(
        items.begin(),
        items.end(),
        [](auto const&amp; lhs, auto const&amp; rhs) {
            return
                std::invoke(&amp;Type::id, lhs) &gt;
                std::invoke(&amp;Type::id, rhs);
        }
    );
}

// (1) can sort a vector of items
std::vector&lt;Item&gt; items = /*...*/;
SimpleSort(items);

// (2) can sort a vector of pointers to items
std::vector&lt;Item*&gt; items = /*...*/;
SimpleSort(items);

// (3) can sort a vector of reference_wrappers to items
std::vector&lt;std::reference_wrapper&lt;Item&gt;&gt; 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&lt;Item&gt; items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &amp;Item::id);

// (2) sort a vector of pointers to items
std::vector&lt;Item*&gt; items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &amp;Item::id);

// (3) sort a vector of reference_wrappers to items
std::vector&lt;std::reference_wrapper&lt;Item&gt;&gt; items = /*...*/;
std::ranges::sort(items, std::ranges::greater(), &amp;Item::id);

huangapple
  • 本文由 发表于 2023年3月9日 19:35:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/75684053.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定