- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我遇到了一个似乎非常出乎意料的重载解析行为。以下代码被 gcc 和 clang 拒绝并出现歧义错误:
template <typename T>
struct A
{
typedef T key_type;
};
template <typename T>
void foo(A<T> rng, T val);
template <typename T, typename U = T>
void foo(T, typename U::key_type);
int main()
{
A<int> i;
foo(i, 0);
}
错误是:
test.cpp:16:5: error: call to 'foo' is ambiguous
foo(i, 0);
^~~
test.cpp:8:6: note: candidate function [with T = int]
void foo(A<T> rng, T val);
^
test.cpp:11:6: note: candidate function [with T = A<int>, U = A<int>]
void foo(T, typename U::key_type);
^
我希望两者完全匹配,但第一个重载在部分排序中获胜,因为在第一个参数中 A<T>
比 T
更专业.
令我震惊的是,如果我将第二个签名更改为:
template <typename T, typename U = T>
void foo(T, typename T::key_type);
gcc 和 clang 现在都接受代码,并选择我最初期望的第一个重载。
我看不出这种变化如何影响行为:我所做的只是用默认值 ( U
) 替换既未明确指定也未推导的模板参数 ( T
) .
话又说回来,更改前的行为从一开始就出乎意料,所以也许我遗漏了什么。
谁能解释一下:
如果相关,我测试的编译器版本是 gcc 4.8.0 和最近的 clang 主干构建。
最佳答案
问题是在参数推导之后是否存在将推导的模板参数替换到参数列表中的阶段。此阶段是默认参数将用于尚未推导的模板参数的地方。
关于这个额外步骤完成了哪些推导上下文以及没有完成什么的问题是一个活跃的核心问题,http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#697 .
如果执行额外的替换步骤,则还需要实例化模板(否则替换步骤本身就没有多大意义)。您也可以只选择默认参数,而不进行替换,但在标准中,这两者是结合在一起的,所以作为一个实现者,我不会选择那条路。
部分排序在很大程度上独立于执行部分排序的上下文(考虑了一些依赖于上下文的事情——例如,忽略没有显式调用参数的函数参数)。它也独立于模板参数是否传递了显式模板参数(因此,如果您给 U
赋值,偏序将不会“记住”它。
Clang 和 GCC 不执行替换步骤,也不使用模板默认参数。因此,当将 T
与 U::key_type
进行比较以找出 U
时,他们会说“嗯,一个非推导的上下文。我们将说“成功,没有什么不匹配的!”对于这个参数”。将 T
与 T::key_type
进行比较时也会发生同样的情况。当它比较另一个方向时,WhatEver::key_type
与 T
,T
也可以推断出依赖类型。因此,对于第二个参数,在您的两次尝试中,两个模板至少彼此一样专业。
但重要的区别在于,推导后,参数类型列表中使用的所有参数必须有值。
In most cases, all template parameters must have values in order for deduction to succeed, but for partial ordering purposes a template parameter may remain without a value provided it is not used in the types being used for partial ordering. [ Note: A template parameter used in a non-deduced context is considered used. — end note ]
在你的第二次尝试中,T
是由第一个参数推导出来的,所以在完成参数/参数类型的比较之后没有发生任何不好的事情。在第一次尝试中,U
未被推导,因此第一个模板在之后并不被认为比第二个模板“更专业”。
关于c++ - 使用默认函数模板参数的意外重载决议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15697067/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!