【发布时间】:2016-08-25 16:37:06
【问题描述】:
我无法理解 C++ 标准中转换函数模板参数推导规则的含义。该标准规定([temp.deduct.conv] 第 1 条,N4594 中的 §14.8.2.3.1):
模板参数推导是通过将转换函数模板的返回类型(称为 P)与转换结果所需的类型(称为 A;参见 8.5、13.3.1.5 和 13.3)进行比较来完成的。 1.6 用于确定该类型)如 14.8.2.5 中所述。
其中 14.8.2.5 ([temp.deduct.type]) 是描述一般模板参数推导的部分(尽管最常见的情况,函数调用模板参数推导 [temp.deduct.call],似乎不再指向那里;有过吗?)。不过,下一个条款让我感到困惑(第 2 条):
如果 P 是引用类型,则使用 P 引用的类型代替 P 进行类型推导以及本节其余部分对 P 的任何进一步引用或转换。
对我来说,这似乎意味着 template <class T> operator T() 和 template <class T> operator T&() 是相同的(如果同时指定两者会导致歧义)。但在我使用的任何编译器中都不是这样!例如:
struct any1 { template <typename T> operator T() { } };
struct any2 { template <typename T> operator T&() { } };
void f1(int) { }
void f2(int&) { }
void f3(int const&) { }
int main() {
f1(any1());
// f2(any1()); compile time error
f3(any1());
f1(any2());
f2(any2());
f3(any2());
}
但是如果引用被忽略,any1 和 any2 应该有相同的行为,对吧?显然他们没有,因为f2(any1()) 不能使用 gcc 或 clang 编译,而 f2(any2()) 可以同时编译。
下一个子句(第 3 条,尤其是第 3.3 条)更加混乱:
如果 A 不是引用类型:[...] 如果 P 是 cv 限定类型,则类型推导时忽略 P 类型的顶级 cv 限定符。
这以及关于删除引用的第 2 条似乎暗示以下代码由于歧义而不应编译:
struct any3 {
template <typename T> operator T&() { }
template <typename T> operator T const&() { }
};
void f1(int) { }
int main() {
f1(any3());
}
然而这对 gcc 和 clang 都适用。
我错过了什么?
编辑
我应该澄清一下,clang 和 gcc 编译器处理这个问题的方式正是我对 C++ 的一般(相对高级)理解所期望的。一些评论者要求澄清我的困惑是什么(以及隐含的,为什么我应该关心)。我在这里的困惑完全与试图理解标准的含义有关。我需要对此有一个清晰的了解,因为我提交的论文中的代码在很大程度上依赖于这项工作以及我对它的使用是否符合标准。
【问题讨论】:
-
请说明您的第一个代码中哪些是意外的,哪些是预期的。您是否期望注释行编译?其他线路的意义是什么?据我所知,注释行的
T是int。intprvalue 不能用于初始化int&。虽然T&和T情况的扣除可能相同,但转换函数将在一种情况下返回引用,而在另一种情况下则不会。这对我来说似乎很清楚。 -
@JohannesSchaub-litb 澄清了,谢谢。关键是如果
P中的引用被忽略,正如标准似乎暗示的那样,那么any1和any2应该具有相同的行为。第一个代码的工作方式与我在阅读标准之前的猜测一样(即纯右值不能绑定到左值引用,但左值引用可以),但它与我从阅读中得到的不一致标准。这就是混淆点——标准文档本身的解释 -
对于第二种情况,我认为第二种模板更专业,因此它将被选择用于用户定义的转换序列(这是决胜局之一,否则会产生歧义) .
-
我不明白你的困惑..
template<typename T> void f(T&);是否也会让你感到困惑,因为它不允许f(1)?在类型推导过程中也忽略了引用。这并不意味着在将int替换为T&之后它不再存在。 -
@JohannesSchaub-litb 不,这在 14.8.2.2 中是被禁止的。函数参数推导部分(14.8.2.2)没有与 14.8.2.5 中的第 2 条类似的子句
标签: c++ templates standards type-deduction