转发参考和正常参考的部分排序与推导指南
Partial ordering of forwarding reference and normal reference with deduction guides
gcc 8.0.0 and clang 5.0.0 不同意此程序的行为:
#include <iostream>
template <typename T>
struct A {
A(const T&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
A(T&&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
};
template <typename U> A(U&&) -> A<double>;
int main() {
int i = 0;
const int ci = 0;
A a1(0); // both say A<double>
A a2(i); // both say A<double>
A a3(ci); // gcc says A<int>, clang says A<double>
}
gcc 的行为对我来说没有意义 - 如果 const T&
重载优于 U&&
左值 const int
重载,为什么 T&&
重载优于右值 int
的 U&&
重载? clang 对我来说更有意义(none 的功能比其他功能更专业,因此演绎指南获胜)。
谁说得对?
又到了偏序地。合成函数模板参数类型为
T&& // #1: not a forwarding reference
const T& // #2
U&& // #3: a forwarding reference
偏序前转换 strips away referenceness and after that the top-level cv-qualification, leaving us with a bare type in all three cases. It follows that in all three cases deduction succeeds in both directions. We are now left with [temp.deduct.partial]/9 的决胜局:
If, for a given type, deduction succeeds in both directions (i.e., the
types are identical after the transformations above) and both P and A
were reference types (before being replaced with the type referred to
above):
- if the type from the argument template was an lvalue
reference and the type from the parameter template was not, the
parameter type is not considered to be at least as specialized as the
argument type; otherwise,
- if the type from the argument template
is more cv-qualified than the type from the parameter template (as
described above), the parameter type is not considered to be at least
as specialized as the argument type.
对于 U&&
与 T&&
,这两个规则都不适用,也没有排序。但是,对于 U&&
与 const T&
,根据第一个项目符号,参数类型 U&&
不被认为至少与参数类型 const T&
一样专业。
因此,部分排序发现 #2 比 #3 更专业,但发现 #1 和 #3 无法区分。 GCC 是正确的。
也就是说,这很可能是对部分排序规则的疏忽。 Class模板推导是我们第一次有"rvalue reference to cv-unqualified template parameter that isn't a forwarding reference"的东西。以前,在双重引用的情况下,转发引用总是会在第二个项目符号处输给非转发右值引用(因为你获得非转发右值引用的唯一方法是如果你有 cv T&&
一些非空 cv
).
gcc 8.0.0 and clang 5.0.0 不同意此程序的行为:
#include <iostream>
template <typename T>
struct A {
A(const T&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
A(T&&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
};
template <typename U> A(U&&) -> A<double>;
int main() {
int i = 0;
const int ci = 0;
A a1(0); // both say A<double>
A a2(i); // both say A<double>
A a3(ci); // gcc says A<int>, clang says A<double>
}
gcc 的行为对我来说没有意义 - 如果 const T&
重载优于 U&&
左值 const int
重载,为什么 T&&
重载优于右值 int
的 U&&
重载? clang 对我来说更有意义(none 的功能比其他功能更专业,因此演绎指南获胜)。
谁说得对?
又到了偏序地。合成函数模板参数类型为
T&& // #1: not a forwarding reference
const T& // #2
U&& // #3: a forwarding reference
偏序前转换 strips away referenceness and after that the top-level cv-qualification, leaving us with a bare type in all three cases. It follows that in all three cases deduction succeeds in both directions. We are now left with [temp.deduct.partial]/9 的决胜局:
If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above):
- if the type from the argument template was an lvalue reference and the type from the parameter template was not, the parameter type is not considered to be at least as specialized as the argument type; otherwise,
- if the type from the argument template is more cv-qualified than the type from the parameter template (as described above), the parameter type is not considered to be at least as specialized as the argument type.
对于 U&&
与 T&&
,这两个规则都不适用,也没有排序。但是,对于 U&&
与 const T&
,根据第一个项目符号,参数类型 U&&
不被认为至少与参数类型 const T&
一样专业。
因此,部分排序发现 #2 比 #3 更专业,但发现 #1 和 #3 无法区分。 GCC 是正确的。
也就是说,这很可能是对部分排序规则的疏忽。 Class模板推导是我们第一次有"rvalue reference to cv-unqualified template parameter that isn't a forwarding reference"的东西。以前,在双重引用的情况下,转发引用总是会在第二个项目符号处输给非转发右值引用(因为你获得非转发右值引用的唯一方法是如果你有 cv T&&
一些非空 cv
).