5

给定以下函数模板:

#include <vector>
#include <utility>

struct Base { };
struct Derived : Base { };

// #1
template <typename T1, typename T2>
void f(const T1& a, const T2& b)
{
};

// #2
template <typename T1, typename T2>
void f(const std::vector<std::pair<T1, T2> >& v, Base* p)
{
};

为什么下面的代码总是调用重载#1 而不是重载#2?

int main()
{
    std::vector<std::pair<int, int> > v;
    Derived derived;

    f(100, 200);  // clearly calls overload #1
    f(v, &derived);         // always calls overload #1

    return 0;
}

鉴于 of 的第二个参数f是 的派生类型Base,我希望编译器会选择重载 #2,因为它比重载 #1 中的泛型类型更匹配。

是否有任何技术可以用来重写这些函数,以便用户可以编写main函数中显示的代码(即,利用编译器推导参数类型)?

4

3 回答 3

12

您可以这样做:

f(v, static_cast<Base*>(&derived));

或者使用 SFINAE 删除第一个函数作为选择候选:

// Install boost library and add these headers:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>

// #1 - change it to look like this (note the keyword void changed positions)
template <typename T1, typename T2>
typename boost::disable_if<
   typename boost::is_convertible<T2, Base*>, void>::type
f(const T1& a, const T2& b)
{
};

// #2 - this one can stay the same
template <typename T1, typename T2>
void f(const std::vector<std::pair<T1, T2> >& v, Base* p)
{
};
于 2009-10-14T18:11:21.370 回答
8

假设 f 的第二个参数是 Base 的派生类型

它可以转换为此类,但它是 Derived*。第一个模板函数不需要转换,第二个需要转换,因此它选择第一个。

这选择了第二个:

f(v, static_cast<Base*>(&derived));

在旁注中,main返回一个int.

于 2009-10-14T17:53:57.083 回答
1

除了关于Koenig Lookup的明显主题(编译器或多或少都很好地实现了)(尤其是旧的很成问题)之外,还有一些关于模板专业化的陷阱。

专业化要求类型完全匹配(不确定标准是如何定义的,但根据我的经验 [gcc, msvc] 派生类将不匹配)。如果您向 Base* 添加一个丑陋的演员表,它应该像您预期的那样工作,可以选择为 Derived 添加另一个专业化......

于 2009-10-14T18:09:12.747 回答