添加过载更改选择了哪个过载

时间:2018-09-20 15:49:51

标签: c++

我试图了解重载选择规则如何导致以下(不直观)行为。当我具有以下功能时:

#include <iostream>

// Overload 1
template<class T>
void write(T data)
{
  std::cout << "Called write(T data)" << std::endl;
}

// Overload 2
template<class T, class ...U>
void write(T&& obj, U&&... objs)
{
  std::cout << "Called write(T&& obj, U&&... objs)" << std::endl;
}

int main(int, char**)
{
  int j = 0;
  write(j);
  return 0;
}

已选择void write(T data)重载(重载1)。我认为这很有意义:重载选择的候选者是void write<T>(T) T = intvoid write<T,U>(T&) T = int, U = <>write(T)write(T&)都将同样专门化,但是Overload 2具有空参数包,因此选择了Overload 1。但是,如果我添加了第三个重载:

#include <iostream>

// Overload 0
void write(const int& data)
{
  std::cout << "Called write(const int& data)" << std::endl;
}

// Overload 1
template<class T>
void write(T data)
{
  std::cout << "Called write(T data)" << std::endl;
}

// Overload 2
template<class T, class ...U>
void write(T&& obj, U&&... objs)
{
  std::cout << "Called write(T&& obj, U&&... objs)" << std::endl;
}

int main(int, char**)
{
  int j = 0;
  write(j);
  return 0;
}

然后突然被调用void write(T&& obj, U&&... objs)(过载2)。为什么添加未选择的重载会更改实际上选择的重载?

如果唯一的候选者是void write<T,U>(T&) T = int, U = <>void write(const int&),我知道为什么会选择void write<T,U>(T&),所以也许通过增加额外的重载可以防止void write(T data)从参与过载选择?如果可以,为什么?

由于这似乎是编译器特有的行为,因此在gcc 7.3.0上可以观察到。

一些更有趣的行为: 如果对函数进行了重新排序,以便将新的重载置于原始两个重载之间(即,重载1,然后重载0,然后重载2),然后gcc用call of overloaded ‘write(int&)’ is ambiguous拒绝它。如果对函数进行了重新排序,以使新的重载位于最后(即重载1,然后重载2,然后重载0),然后选择write(const int& data)

1 个答案:

答案 0 :(得分:1)

我认为这是一个GCC错误:

超载是:

  • 过载0:write(const int&)
  • 过载1:write(T) [T=int] -> write(int)
  • 过载2:write(T&&,U&&...) [T=int&,U=[]] -> write(int&)

重载0比重载1更好,因为重载0不是模板函数的专业化。

重载1比重载2更匹配,因为重载1是比重载2更专门的功能模板。

重载2比重载0更匹配,因为重载2 int&的参数类型的cv限定词是重载0:const int&的子集。

因此,通话不明确,如Clang所报道。


为简化起见,在比较两个函数时,此处分四个步骤评估了最佳可行的函数:

  1. 检查哪个转换顺序最好(所有转换都是此处的身份转换),然后转换排名是否相同:
  2. 检查两个参考绑定之间是否一个比另一个更好,然后如果一个不是参考绑定或两个绑定不可区分,
  3. 检查函数之一是否是模板专业化,而另一个不是模板专业化
  4. 检查其中一个专业是否比另一个专业更专业。