Pap*_*ter 12 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'; }
// Usage
foo<0>(); // Ambiguous, int or short?
Run Code Online (Sandbox Code Playgroud)
起初我对这种行为并不感到惊讶,文字0可能是一个int或一个short,但如果我们尝试这个:
// Definition
void foo(int i) { std::cout << "i: " << i << '\n'; }
void foo(short s) { std::cout << "s: " << s << '\n'; }
// Usage
foo(0); // "i: 0"!
Run Code Online (Sandbox Code Playgroud)
这个电话foo是明确无误的!它需要int过载(即使模板版本没有).好了,经过一番思考,这不是一个令人惊讶的行为,毕竟没有办法指定一个short文字所以编译器认为这0是一个int(这是默认行为AFAIK),以便明确地调用short非版本模板foo我们可以显式实例化short:
foo(0); // "i: 0"
foo(short{0}); // "s: 0"
Run Code Online (Sandbox Code Playgroud)
所以我认为这将明确模板化的版本,但它没有:
foo<int{0}>(); // Ambiguous call, candidates are int and short versions
foo<short{0}>(); // Ambiguous call, candidates are int and short versions
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)call of overloaded 'foo()' is ambiguous foo<int{0}>(); note: candidates are: void foo() [with int I = 0] void foo() [with short int S = 0] call of overloaded 'foo()' is ambiguous foo<short{0}>(); note: candidates are: void foo() [with int I = 0] void foo() [with short 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;
}
Run Code Online (Sandbox Code Playgroud)
没有成功,你可以......所以,问题是什么?
foo是模棱两可的?(注意,没有模板foo需要int的版本,所以是明确的).foo即使在指定呼叫类型后,对模板的调用仍然不明确?(注意,没有模板foo 工作正常).谢谢.
编译器错误消息是missleading*.你会直观地认为这意味着"函数调用是一个暧昧的!",但实际上编译在早期阶段失败了,那时甚至没有生成专用函数的定义.
它的真正含义是:"功能专业化是暧昧的!"
让我们看看这是如何编译的:
template <short S> void foo() { std::cout << "S: " << S << '\n'; }
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译的第一步是模板专业化.
第1步:编译器意识到这foo<0>是一个模板特化,并相应地生成一个函数声明:
template <short S> void foo() { std::cout << "S: " << S << '\n'; }
template<>
void foo<0>();
int main(int argc, char* argv[])
{
foo<0>();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
第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';
}
Run Code Online (Sandbox Code Playgroud)
第3步:现在你有一个可调用的函数,编译继续正常.
让我们尝试按照您的情况执行相同的步骤:
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;
}
Run Code Online (Sandbox Code Playgroud)
第1步:生成函数声明:
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;
}
Run Code Online (Sandbox Code Playgroud)
此时编译失败了,因为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[])
{
return 0;
}
Run Code Online (Sandbox Code Playgroud)
没有函数调用,您将收到相同的错误消息!
更新
所以外卖是一切都转化为专门的函数声明.所以,不管你写foo<int{0}>的foo<short{0}>,编译器会生成template<> void foo<0>();两个.显式类型将被忽略.(这就是为什么它们真的很重要 - 它们是constexpr-s.)
更新 正如TC在他的评论中指出的那样,在标准(PDF中的第413页)中有一个非常相似的例子:
[示例:在以下示例中,假设signed char不能表示值1000,将int类型的template-argument转换为signed char需要缩小转换(8.5.4),因此第二个模板的替换失败( 14.3.2).
Run Code Online (Sandbox Code Playgroud)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- 末端的例子]
*错误消息完全正确.可能不是特别直观,但它反映了标准中规定的程序. - TC
这是你写作时会发生的事情f<0>().
编译器查找f,找到两个函数模板声明:
template <int I> void foo();
template <short S> void foo();
Run Code Online (Sandbox Code Playgroud)编译器看到显式模板参数列表并尝试将其替换为每个函数模板声明:
template <int I> void foo(); // with I = 0
template <short S> void foo(); // with S = 0
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,替换都会成功,因为它0是一个int,并且可以转换为short,并且转换是在此上下文中允许的转换.
替换后,产生两个候选函数专精.两者都是可行的.然后执行过载分辨率 - 由于签名相同且没有应用仲裁器,因此重载解析失败并且调用不明确.
这里的要点是正常的重载决策规则不适用于模板参数.模板参数的转换在常规重载解析发生之前的早期阶段应用.
| 归档时间: |
|
| 查看次数: |
506 次 |
| 最近记录: |