我试图了解重载选择规则如何导致以下(不直观)行为。当我具有以下功能时:
#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 = int
和void 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)
。
答案 0 :(得分:1)
我认为这是一个GCC错误:
超载是:
write(const int&)
write(T) [T=int] -> write(int)
write(T&&,U&&...) [T=int&,U=[]] -> write(int&)
重载0比重载1更好,因为重载0不是模板函数的专业化。
重载1比重载2更匹配,因为重载1是比重载2更专门的功能模板。
重载2比重载0更匹配,因为重载2 int&
的参数类型的cv限定词是重载0:const int&
的子集。
因此,通话不明确,如Clang所报道。
为简化起见,在比较两个函数时,此处分四个步骤评估了最佳可行的函数: