【发布时间】:2021-01-05 06:24:05
【问题描述】:
转换函数模板推导模板参数的规则在temp.deduct.conv部分。
第一条规则指定对应的P和A是什么,第一个问题在这里:
模板参数推导是通过将转换函数模板的返回类型(称为 P)与 正在查找的转换函数 ID 的转换类型 ID 指定的类型(称为A) 如 [temp.deduct.type] 中所述。如果在重载解析期间构造转换函数 ID ([over.match.funcs]),则适用以下转换。
#include <iostream>
struct C{
template<typename Type>
operator Type(){
Type a{};
/*a = 1;*/
return a;
}
};
int main(){
int const& rf = C{};
}
对于这个例子,P 应该是Type,但是 A 是什么?似乎只有一条规则与之相关:
over#match.funcs.general-7
在每个考虑类 S 的转换函数来初始化 T 类型的对象或引用的情况下,候选函数包括在 S 中搜索 conversion-function-id 运算符 T 的结果强>。
我不知道这里的 A 是只是 int const& 还是 为此初始化定义的任何允许的类型。
如果A由前者决定,则P为Type,A为int const&。
根据项目符号 2:
如果 P 是引用类型,则使用 P 引用的类型代替 P 进行类型推导以及在本小节的其余部分中对 P 的任何进一步引用或转换。
因此,转换后的 P 仍然是Type,那么这样的 P/A 对将经历第 3 条
如果 A 不是引用类型:
- [...]
因为 A 是引用类型,所以跳过第 3 条,继续第 4 条
如果 A 是 cv 限定类型,则忽略 A 类型的顶级 cv 限定符进行类型推导。如果A是引用类型,则A引用的类型用于类型推导。
执行第 4 点,现在 A 转换为 int const。
第 5 条说:
一般来说,推导过程会尝试找到使推导的 A 相同与 A 相同的模板参数值。但是,可能会忽略 A 的某些属性:
- 如果原始 A 是引用类型,则 A 的任何 cv 限定符(即引用所引用的类型)。
- 如果原始 A 是函数指针或指向成员函数的指针类型,则为 noexcept。
- A 中可以通过限定转换恢复的任何 cv 限定符。
这些只有在类型推导失败时才会被忽略。
这里有个问题,compiler 说推导的 A 是int,但是,IIUC,从const int 推导出Type,推导的类型难道不是const int?为什么推导出的模板参数是int 而不是const int?
如果将转换函数模板改为
#include <iostream>
struct C{
template<typename Type>
operator Type&(){
static Type a{};
a = 1;
return a;
}
};
int main(){
int const& rf = C{};
}
那么Type将被推导出为const int。为什么此时推导的类型与上面的示例不同? (它们应该是相同的结果,即const int)。在第一个示例中,我从“const int”中推断出Type 有什么遗漏吗?
【问题讨论】:
-
@LanguageLawyer 那么,我们可以认为第二个问题是当前标准的缺陷吗?第一个问题呢,我觉得判断
A的第一条规则的写法比c++17对应的要复杂。 -
我不知道这里的 A 是只是 int const& 还是为此初始化定义的任何允许的类型。 为什么会这样?
-
@LanguageLawyer 它说“正在查找的转换函数 ID 的转换类型 ID”。对于
int const&类型的初始化引用,查找的候选对象不仅包含operator int const&(),还包含operator int&。正如over.match.ref#1 中所说。此外,为了初始化 int 类型的对象,应查找候选对象不仅包含operator int(),还应查找包含operator cv int()和operator int cv&()和operator int cv&&(),如over.match.funcs#general-7 中所述
标签: c++ templates language-lawyer