为什么我不能将两个不同的比较器传递给一个模板函数?

时间:2016-10-21 11:21:01

标签: c++ templates comparator c2664

我在这里唠叨了几个小时,但我仍然不明白为什么我在尝试运行此代码时遇到错误。 过了一段时间,我设法将其缩小到表达式:

pastryPrice()

导致问题 - 正如你所看到的,我正在尝试为一个模板排序函数构建大量的比较器

    struct dialingAreaComp{
    inline bool operator()(const Deliver *d1, const Deliver *d2)const {
        return d1->getDialingArea() < d2->getDialingArea();
    }
};
struct pastryPrice {
    inline bool operator()(const Pastry *p1, const Pastry *p2)const {
        return p1->getPrice() < p2->getPrice();
    }
};
template<class T>
void sortCollection(T& collection)
{
    if ( typeid (collection) == typeid(vector <Deliver*>))
    {
        sort(collection.begin(), collection.end(), dialingAreaComp());
        printCollection(collection);
    }
    else if (typeid (collection) == typeid(vector <Pastry*>))
    {
        sort(collection.begin(), collection.end(), pastryPrice());
        printCollection(collection);
    }
    else { cout << "WRONG!"; }
}

我得到了五个错误,完全相同:

  

严重级代码描述项目文件行抑制状态   错误C2664'bool Bakery :: pastryPrice :: operator()(const Pastry *,const Pastry *)const':无法将参数1从'Deliver *'转换为'const Pastry *'Bakery c:\ program files(x86)\ microsoft visual studio 14.0 \ vc \ include \ xutility 809

还有一个:

  

严重级代码描述项目文件行抑制状态   错误C2056非法表达面包店c:\ program files(x86)\ microsoft visual studio 14.0 \ vc \ include \ xutility 809

当我取消上面写的表达式时,代码工作很好 - 为什么我不能将两个不同的比较器传递给一个模板函数?

现在:

  

C2264是一个编译器错误,当一个人试图传递一个不兼容类型的参数时会发生。

但Deliver功能正常工作,当我取下Pasiver编译的Deliver比较器时...那么什么是不兼容的类型?

2 个答案:

答案 0 :(得分:5)

你的问题是两个分支都被编译,无论采取哪一个分支。

我会采用不同的方法。

template<class A, class B>
struct overload_t:A,B{
  using A::operator();
  using B::operator();
  overload_t(A a, B b):A(std::move(a)), B(std::move(b)){}
};
template<class A, class B>
overload_t<A,B> overload( A a, B b ){
  return {std::move(a),std::move(b)};
}

这让我们重载两个函数对象或lambdas。 (可以添加完美的转发,varargs ......,但我保持简单)。

现在我们只是:

auto comp=overload(dialingAreaComp{}, pastryPrice{});
using std::begin; using std::end;
std::sort( begin(collection), end(collection), comp );

并且编译器为我们选择正确的比较函数。当我在那里的同时支持平面阵列。

停止使用using namespace std;

上面的代码所做的就是将你的两个函数对象拼写为一个。 using A::operator()using B::operator()()移动到同一个类中,并告诉C ++在使用通常的方法调用重载规则调用时在它们之间进行选择。其余代码是粘合剂,用于推断重载类型并移动构造它们。

sort使用基于容器类型的编译时确定类型的对象调用()。重载分辨率(在调用点sort内)然后在编译时选择正确的主体进行比较。

因此,可以扩展技术,支持超过2个重载,函数指针和转发引用。在C ++ 17中,可以做一些工作来让重载类型推导出它的父类型,从而不再需要工厂函数。

答案 1 :(得分:4)

您收到错误,因为模板化函数在编译时被计算,其中一个函数调用永远不会匹配。而不是模板使用简单的函数重载:

void sortCollection(vector <Deliver*>& collection)
{
    sort(collection.begin(), collection.end(), dialingAreaComp());
    printCollection(collection);
}

void sortCollection(vector <Pastry*>& collection)
{
    sort(collection.begin(), collection.end(), pastryPrice());
    printCollection(collection);
}