【问题标题】:why can't i pass two different comparators to one template function?为什么我不能将两个不同的比较器传递给一个模板函数?
【发布时间】:2016-10-21 11:21:01
【问题描述】:

我在这里绞尽脑汁好几个小时,但我仍然不明白为什么在尝试运行此代码时会出现错误。 一段时间后,我设法将其缩小为表达式:

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)\微软视觉工作室 14.0\vc\include\xutility 809

还有一个:

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

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

现在:

C2264 是一种编译器错误,在尝试向函数传递不兼容类型的参数时发生。

但是 Deliver 功能有效,当我取下 Deliver 比较器时,Pastry 也编译了...那么不兼容的类型是什么?

【问题讨论】:

  • 这是因为模板是在编译时评估的,而您的 if 语句是在运行时评估的。所以总是有一个函数调用不匹配。

标签: c++ templates comparator c2664


【解决方案1】:

您的问题是无论采用哪一个分支都会编译两个分支。

我会以不同的方式处理这个问题。

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)};
}

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

现在我们简单地:

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 中,可以做一些工作来让重载类型推断其父类型,从而不再需要工厂函数。

【讨论】:

  • 这对于 OP 来说可能有点矫枉过正。但它看起来很漂亮。通过一些修补,这可能不仅可以接受 2 个重载。
  • @hayt 是的,这很容易。我只是保持简短而甜蜜。
  • @Yakk 这很好!你能说为什么需要构造函数overload_t(A a, B b):A(std::move(a)), B(std::move(b)){}吗?如果在 make-like 函数中没有它,你可以 return {};.
  • @Yakk 是的,我只是在发表评论后才看到提及,并且无法再编辑它......我被代码分心了;)
  • @vsoftco 您必须为构造函数提供 a 和 b 值。 {} 并没有给它任何东西。
【解决方案2】:

您会收到一个错误,因为模板化函数是在编译时评估的,并且其中一个函数调用永远不会匹配。使用简单的函数重载代替模板:

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);
}

【讨论】:

  • 太棒了!谢谢 - 我永远不会猜到 incom
  • *我永远不会猜到这是不兼容的部分......我试图避免创建/调用几个排序函数。
  • @prowler “我试图避免创建/调用几个排序函数。”不会有太大的收获。
  • 不是在资源方面——但我需要向我的讲师展示代码,向她展示我理解代码以及 OOP 在 C++ 中的工作方式。所以...我认为创建一个用于排序的模板函数会很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-05
  • 1970-01-01
  • 2012-07-06
  • 1970-01-01
  • 2020-01-28
相关资源
最近更新 更多