【问题标题】:How to properly declare a generic sorting algorithm?如何正确声明通用排序算法?
【发布时间】:2012-10-23 08:21:24
【问题描述】:

我正在尝试实现合并排序算法:

#include <list>
#include <functional>
#include <iterator>
#include <iostream>
#include <random>

template <typename TIterator, typename TObject>
void mergeSort(const TIterator& begin, const TIterator& end,
               std::function<bool (const TObject& left,
                                   const TObject& right)> criterium)
{
    //...
}

bool compare(int a, int b)
{
    return a < b;
}

int main(int argc, char** argv)  // And now to test the algorithm
{
    std::list<int> container;
    for (int i = 0; i < 100; ++i)
        container.push_back(random() % 20);

    mergeSort(container.begin(), container.end(), compare);

    for (auto it = container.begin(); it != container.end(); ++it)
        std::cout << (*it) << std::endl;

    return 0;
}

此程序无法编译:

error: no matching function for call to 
mergeSort(std::list<int>::iterator, std::list<int>::iterator, bool (&)(int, int))

candidate is:

template<class TIterator, class TObject> 
void mergeSort(const TIterator&, const TIterator&, 
std::function<bool(const TObject&, const TObject&)>)

at global scope

我知道我在声明中搞砸了一些简单的事情,但我无法弄清楚。

【问题讨论】:

    标签: c++ templates c++11


    【解决方案1】:

    std::function&lt;bool(TObject const&amp;, TObject const&amp;)&gt; 中的TObject 不能从任何不是std::function 的参数推导出来,请参阅this question

    您还误用了std::function - 仅当您想存储任何可调用实体时才使用它。如果您只想将任何可调用的内容作为参数,请将其设为模板参数:

    template<class Iter, class Comp>
    void mergeSort(Iter first, Iter last, Comp comp)
    {
      // use 'comp(a, b)'
    }
    

    这也是标准库的处理方式(几乎可以查看每个带有谓词的算法,例如std::sort)。这样,您还可以避免 std::function 执行的(在您的情况下是不必要的)类型擦除。

    【讨论】:

    • 谢谢!我试图将标准声明为附加参数,但它仍然无法编译。我认为,问题出在 TObject 模板参数上。那就是迭代器指向的对象。
    • @Martin:感谢auto,您永远不需要知道TObject 在 C++11 中的真正含义,所以只需像我在答案中所做的那样声明函数。 :)
    • 这种方法可能会为每个新标准实例化整个算法。有没有办法避免这种情况?
    • @Martin:仅当您阅读过早已过时的书籍时。如今,磁盘空间无论如何都很便宜。一个好的链接器也会折叠相同的实例化,如果你不使用模板,那么无论如何都需要为每个TObject 编写任何剩余的东西。
    • @MartinDrozdik:如果 用户 决定他创建了太多不同的模板实例,那么他可以更改他的代码以使用更少的不同类型为 Comp模板参数。一种方法是使用std::function 为每个签名仅获取一个实例化。结果将与您的目标相同,但这是用户的选择,而不是您的算法限制它。每个比较器有不同的实例化并内联它通常是算法的胜利,但通过尝试防止“代码膨胀”,您也可以防止这种情况发生。
    【解决方案2】:

    你可以看看 std::sort:

    template< class RandomIt, class Compare >
    void sort( RandomIt first, RandomIt last, Compare comp );
    

    http://en.cppreference.com/w/cpp/algorithm/sort

    这样你就可以使用任何你想调用的东西。

    【讨论】:

      【解决方案3】:

      您的函数compare 不是std::function,但您的mergeSort 需要一个。 此外,您应该const_iterator 传递给您的函数,因为它应该修改数组。

      如果您更改代码并使用它:

      std::function<bool(const int&, const int&)> myCompare = compare;
      mergeSort(container.begin(), container.end(), myCompare);
      

      它有效(见http://ideone.com/7FdKTP)。

      在我看来,将比较器实现为带有operator() 的结构更容易。这样,您传递的是对象而不是函数,这样更易​​于管理。

      【讨论】:

      • std::function 可以从任何可调用实体构造,所以这不是问题。这里的问题是模板参数推导。
      • 问题出在 std::function 参数上?
      • std::function 的意思是“任何可以用两个相同的参数调用并返回结果隐式转换为布尔值的东西”。这正是我想要的
      • 谢谢!你的建议确实有效!但是为什么我的程序无效?
      • Nitpick:实际上你绝对想要const_iterator,因为sort 必须调整元素。
      【解决方案4】:

      虽然我不知道如何完全解决这个问题,但很明显编译器无法推断出参数。明确说明它们可以作为一种解决方法:

      mergeSort< std::list<int>::iterator, bool >(container.begin(), container.end(), compare);
      

      另一种方法是将您传递的函数显式转换为std::function

      您也可以通过将最后一个参数设为operator&lt; 而不是比较函数来实现这一点,我认为这会更直观。

      【讨论】:

        【解决方案5】:

        mergeSort 需要 const TIterator 而你的不是。

        【讨论】:

        • 确实如此,但在这种情况下不是问题。问题是如果反过来。也就是说,如果我尝试将 const TIterator 传递给未声明为常量的函数。
        • 始终允许将非 const 转换为 const。
        • @MartinDrozdik:实际上,如果函数通过复制获取参数,const 是无关紧要的(对于调用者)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-15
        • 2019-03-21
        相关资源
        最近更新 更多