Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;:重载不会选择预期的方法_C++_Templates_Inheritance_Overloading - Fatal编程技术网

C++ C++;:重载不会选择预期的方法

C++ C++;:重载不会选择预期的方法,c++,templates,inheritance,overloading,C++,Templates,Inheritance,Overloading,我有以下代码: #include <iostream> #include <vector> using namespace std; struct A{}; struct B: public A {}; template <typename T> void foo(const T& obj) { cerr << "Generic case"<< endl;} void foo(const A& a) { c

我有以下代码:

#include <iostream>
#include <vector>
using namespace std;

struct A{};
struct B: public A {};

template <typename T>
void foo(const T& obj) { cerr << "Generic case"<< endl;}

void foo(const A& a) {
    cerr << "Specific case" << endl;
}

int main() {
    vector<int> v;
    foo(v);
    B b;
    foo(b);
    A a;
    foo(a);
}
代码编译后,输出为:

Specific case
Specific case
为什么模板化方法的存在会产生如此大的差异

编辑:如何强制编译器为存在时从中派生的类选择自由方法
模板化方法的调用?

对模板实例化产生的
foo(const B&)
的调用不需要转换,因此它是更好的匹配

当编译器看到函数调用时,必须实例化每个基函数模板,并将其与每个普通函数一起包含在重载集中。然后执行重载解析。还有SFINAE,它允许函数模板的实例化导致错误(这样的函数不会添加到重载集)。当然,事情并不是那么简单,但它应该给出总体情况

关于编辑:只有一个方法可调用。还有什么可以作为输出?

@pmr's解释了为什么在您的示例中首选模板函数。要强制编译器选择重载,可以使用SFINAE从重载集中删除模板函数。将模板化的
foo
更改为

template <typename T>
typename std::enable_if<!std::is_base_of<A, T>::value>::type
  foo(const T& obj) { cerr << "Generic case"<< endl;}
模板

typename std::enable_iftype_traits
头。

是的,这有点令人惊讶,但在重载解析方面,继承和模板并没有很好地结合

问题是,在评估应该选择哪个重载时,编译器选择需要最少转换的重载(内置到内置、派生到基、调用非显式构造函数或转换运算符等)。排名算法实际上相当复杂(并非所有转换都被视为相同的…)

一旦对重载进行了排序,如果最上面的两个排序相同,并且其中一个是模板,则该模板将被丢弃。但是,如果模板的级别高于非模板(通常转换次数较少),则会选择该模板

就你而言:

  • 对于
    std::vector
    而言,只有一个重载匹配,因此选择它
  • 对于
    A
    两个重载匹配,它们的排名相等,模板一被丢弃
  • 对于
    B
    两个重载匹配,模板排名较高(不需要派生到基的转换),将选择它
有两种解决方法,最简单的是“修复”呼叫站点:

A const& ba = b;
foo(ba);
另一个是修复模板本身,但这是棘手的

您可以硬编码,对于从
A
派生的类,这不是您希望的重载:

template <typename T>
typename std::enable_if<not std::is_base_of<A, T>::value>::type
foo(T const& t) {
  std::cerr << "Generic case\n";
}
在行动中


这在C++03中也是可能的,但稍微麻烦一些。想法是一样的,省略号参数
的排名最差,因此我们可以对另一个函数使用重载选择来驱动对主函数的选择。

是-只有一个方法可以简化事情。我想我也希望const B&call在这种情况下失败。
B
a
的直接子对象。它可以绑定到
A&
const A&
。现在我意识到我已经花了大约31分钟来编写我的答案。。。因此,我的时间被消耗殆尽:x@MatthieuM. 我为你感到难过。如果有索菲亚·安蒂波利斯的聚会,我会请你喝杯啤酒;)我不是也要从foo()那里退一些东西吗?如果我想保持方法签名不变呢。由于foo()的方法体,是否可以更改foo()内部的代码并使用enable_if来防止实例化?@ATemp
enable_if
有两个模板参数,其中第二个是返回类型。它默认为
void
,这就是我在示例中省略它的原因。如果您有不同的返回类型,只需添加第二个模板参数。由于模板函数的主体中存在代码,因此无法阻止在重载解析期间选择模板函数。
template <typename T>
typename std::enable_if<not std::is_base_of<A, T>::value>::type
foo(T const& t) {
  std::cerr << "Generic case\n";
}
// Utility
template <typename T, typename Result = void>
struct enable: std::enable_if< std::is_same<T, std::true_type>::value > {}; 

template <typename T, typename Result = void>
struct disable: std::enable_if< not std::is_same<T, std::true_type>::value > {}; 
std::false_type has_specific_foo(...);

template <typename T>
auto foo(T const& t) -> typename disable<decltype(has_specific_foo(t))>::type {
  std::cerr << "Generic case\n";
}
std::true_type has_specific_foo(A const&);