C++ 重载的非类型模板不明确,而非模板化函数正常
如果我们有一个模板函数,它接受类型为C++ 重载的非类型模板不明确,而非模板化函数正常,c++,templates,non-type,C++,Templates,Non Type,如果我们有一个模板函数,它接受类型为int或short的非类型参数,编译器会抱怨以下调用的不确定性: // Definition template <int I> void foo() { std::cout << "I: " << I << '\n'; } template <short S> void foo() { std::cout << "S: " << S << '\n'; } //
int
或short
的非类型参数,编译器会抱怨以下调用的不确定性:
// Definition
template <int I> void foo() { std::cout << "I: " << I << '\n'; }
template <short S> void foo() { std::cout << "S: " << S << '\n'; }
// Usage
foo<0>(); // Ambiguous, int or short?
对foo
的调用是明确的!它接受int
重载(即使模板版本没有)。嗯,想一想,这并不是一个令人惊讶的行为,毕竟没有办法指定short
文本,因此编译器认为0
是int
(这是默认行为AFAIK),为了明确地调用非模板化的foo
版本的short
,我们可以显式地实例化short
:
foo(0); // "i: 0"
foo(short{0}); // "s: 0"
因此,我认为这将明确模板化版本,但事实并非如此:
foo<int{0}>(); // Ambiguous call, candidates are int and short versions
foo<short{0}>(); // Ambiguous call, candidates are int and short versions
foo();//调用不明确,候选是int和short版本
foo();//调用不明确,候选是int和short版本
重载“foo()”的调用不明确
foo();
注:候选人包括:
void foo()[带int I=0]
void foo()[具有短int S=0]
重载“foo()”的调用不明确
foo();
注:候选人包括:
void foo()[带int I=0]
void foo()[具有短int S=0]
我最后尝试的是使用实例而不是文字:
template <int I> void foo() { std::cout << "I: " << I << '\n'; }
template <short S> void foo() { std::cout << "S: " << S << '\n'; }
void foo(int i) { std::cout << "i: " << i << '\n'; }
void foo(short s) { std::cout << "s: " << s << '\n'; }
constexpr int i{1};
constexpr short s{5};
int main()
{
foo(i); // "i: 1"
foo(s); // "s: 5"
foo<i>(); // Ambiguous! (expected "I: 1")
foo<s>(); // Ambiguous! (expected "S: 5")
return 0;
}
template void foo(){std::cout这里的模板函数是按值参数化的,并且只按值参数化,不按类型参数化!更新:现在我不确定。
另一方面,未模板化的版本是按类型参数化的(人们可以享受多态调用)
更新:
好的,看起来实例化的函数名称被破坏实际上取决于数值模板参数的类型。编译器错误消息是错误的*。你会直觉地认为这意味着“函数调用是模糊的!”,但实际上,编译在早期阶段失败,甚至在那时还没有生成专用函数的定义
它真正的意思是:“功能专门化是模糊的!”
让我们看看它是如何编译的:
template <short S> void foo() { std::cout << "S: " << S << '\n'; }
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
-[结束示例]
*错误消息完全正确。可能不是特别直观,但它反映了标准中指定的过程。-T.C.以下是编写f()
时发生的情况
编译器查找f
,并找到两个函数模板声明:
template <int I> void foo();
template <short S> void foo();
模板void foo();
模板void foo();
编译器会看到显式模板参数列表,并尝试将其替换到每个函数模板声明中:
template <short S> void foo() { std::cout << "S: " << S << '\n'; }
template <int I> void foo() { std::cout << "I: " << I << '\n'; }
template<>
void foo<0>();
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
template <int I> void foo(); // with I = 0
template <short S> void foo(); // with S = 0
模板void foo();//I=0
模板void foo();//带S=0
在这两种情况下,替换都会成功,因为0
是一个int
,可以转换为short
,并且在这种情况下,转换是允许的转换
替换后,将生成两个候选函数专门化。这两个函数都是可行的。然后执行重载解析-由于签名相同且没有适用的分界线,重载解析将失败,调用不明确
这里的要点是,正常的重载解析规则不适用于模板参数。模板参数的转换是在常规重载解析发生之前的早期阶段应用的。插入代码会得到一些不错的错误消息。我对它们的解释是使用foo(i)时
编译器使用i
类型进行模板推导。使用foo()时
编译器不再使用i
的类型,而是使用它的值,因为模板是
。因为i
的值可以满足两个模板的要求,所以会出现歧义错误。事实上,这是因为现在该值成为f()类型的一部分
;您要求编译器区分foo()
和foo()
(其中一个是int
类型的零,另一个是short
类型的零)。@NathanOliver如您所见,错误消息确实是我粘贴在问题上的消息(事实上,我已经使用ideone测试了所有案例)因此,如果错误消息很好,我无法理解它为什么会失败。那么,显式指定类型int
或short
,在这种情况下为什么不明确?@PaperBirdMaster:之所以不明确,是因为它使用的是提供的参数的值,而不是类型。请检查我明显是int值的位置。What正在发生类似于无法仅基于返回类型重载函数。编译器如何知道您要使用什么?这是一个我想相信的解释,但我认为它不能解释为什么显式值foo()
仍然不明确。@PaperBirdMaster既foo()
又foo()
转换为模板void foo();
。我已经相应地更新了我的答案。很抱歉再次打扰您,Gábor,但是从constepr int I{1}
和constepr short s{5}
用作foo()
和foo()时推断出的显式类型如何
?在这种情况下,为什么现有和明确定义的项的类型不能消除模板的歧义?并且错误消息完全正确。可能不是特别直观,但它反映了标准中指定的过程。此外,template void foo();
是完全有效的。但是,使用显式整数和显式short(consteprinti{1};
和consteprshort s{5};
foo();
和foo();
)没有歧义,因为它们是实例(没有文字)而且没有转换。@PaperBirdMaster这没关系。转换发生在步骤2中,替换仍然成功
template <short S> void foo() { std::cout << "S: " << S << '\n'; }
template<>
void foo<0>();
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
template<>
void foo<0>(){
std::cout << "S: " << 0 << '\n';
}
template <short S> void foo() { std::cout << "S: " << S << '\n'; }
template <int I> void foo() { std::cout << "I: " << I << '\n'; }
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
template <short S> void foo() { std::cout << "S: " << S << '\n'; }
template <int I> void foo() { std::cout << "I: " << I << '\n'; }
template<>
void foo<0>();
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
template <short S> void foo() { std::cout << "S: " << S << '\n'; }
template <int I> void foo() { std::cout << "I: " << I << '\n'; }
template<>
void foo<0>();
int main(int argc, char* argv[])
{
return 0;
}
template <int> int f(int);
template <signed char> int f(int);
int i1 = f<1000>(0); // OK
int i2 = f<1>(0); // ambiguous; not narrowing
template <int I> void foo();
template <short S> void foo();
template <int I> void foo(); // with I = 0
template <short S> void foo(); // with S = 0