给定以下示例:
错误已经在注释中描述过了。
使用了gcc 8.2.1和clang 7.0.1,两者对此示例的操作存在分歧。能否请有人澄清一下情况?
...
具有类类型 (即 T2 是类类型),其中 T1 与 T2 不相关联,并且可以转换为类型“cv3 T3”的 rvalue 或函数 lvalue,其中“cv1 T1”与“cv3 T3”兼容
... 那么第二种情况下的转换结果的值称为转换后的初始化程序。如果转换后的初始化程序是 prvalue,则将其类型 T4 调整为类型“cv1 T4”
否则:
- 如果 T1 或 T2 是类类型,并且 T1 与 T2 不相关联,则使用对象类型“cv1 T1”的复制初始化的用户定义转换考虑规则...根据非引用复制初始化的描述,调用转换函数的结果随后用于直接初始化引用。 对于此直接初始化,不考虑用户定义的转换。
否则,将隐式将初始化表达式转换为类型为“cv1 T1”的 prvalue。 进行暂时材料化转换,然后将引用绑定到结果。
这些规则非常微妙,我无法完全掌握每种情况。对我来说,似乎应该生成 prvalue(我同意 clang 的观点),但关于引用初始化和列表初始化的交互的语言非常模糊。
int g_i = 10;
struct S {
operator int&(){ return g_i; }
};
int main() {
S s;
int& iref1 = s; // implicit conversion
int& iref2 = {s}; // clang++ error, g++ compiles fine:
// `s` is converted
// to a temporary int and binds with
// lvalue reference
int&& iref3 = {s}; // clang++ compiles, g++ error:
// cannot bind rvalue reference
// to lvalue
}
错误已经在注释中描述过了。
使用了gcc 8.2.1和clang 7.0.1,两者对此示例的操作存在分歧。能否请有人澄清一下情况?
否则,如果初始化程序列表只包含一个类型为E的元素,并且T不是引用类型或其引用类型与E相关联,则该对象或引用从该元素处进行初始化(对于拷贝列表初始化采用拷贝初始化,对于直接列表初始化采用直接初始化);如果需要将元素转换为T,则程序发生错误。
否则,如果T是引用类型,则生成所引用类型的prvalue。 prvalue通过拷贝列表初始化或直接列表初始化来初始化其结果对象,这取决于引用的初始化方式。然后使用prvalue来直接初始化引用。[注意:通常情况下,如果引用类型是非const类型的左值引用,则绑定将失败,程序将不合法。— end note]
给定类型“cv1 T1”和“cv2 T2”,如果T1与T2相同,或者T1是T2的基类,则“cv1 T1”与“cv2 T2”相关联。“cv1 T1”与“cv2 T2”引用兼容,如果
- T1与T2关联引用,或者
- T2为“noexcept function”,T1为“function”,其中函数类型是其它情况下相同的,
...稍后出现了一些(个人模棱两可的)关于用户定义转换的语言:
例如:
如果引用是左值引用并且初始化表达式
...
具有一个类类型(即,T2是类类型),其中T1与T2不相关,并且可以转换为类型“cv3 T3”的左值,其中“cv1 T1”与“cv3 T3”引用兼容(通过枚举适用的转换函数([over.match.ref])并通过重载分辨率选择最佳函数来选择该转换),
...
则引用绑定到转换的... 值结果
...
否则,如果初始化表达式...
具有类类型 (即 T2 是类类型),其中 T1 与 T2 不相关联,并且可以转换为类型“cv3 T3”的 rvalue 或函数 lvalue,其中“cv1 T1”与“cv3 T3”兼容
... 那么第二种情况下的转换结果的值称为转换后的初始化程序。如果转换后的初始化程序是 prvalue,则将其类型 T4 调整为类型“cv1 T4”
否则:
- 如果 T1 或 T2 是类类型,并且 T1 与 T2 不相关联,则使用对象类型“cv1 T1”的复制初始化的用户定义转换考虑规则...根据非引用复制初始化的描述,调用转换函数的结果随后用于直接初始化引用。 对于此直接初始化,不考虑用户定义的转换。
否则,将隐式将初始化表达式转换为类型为“cv1 T1”的 prvalue。 进行暂时材料化转换,然后将引用绑定到结果。
这些规则非常微妙,我无法完全掌握每种情况。对我来说,似乎应该生成 prvalue(我同意 clang 的观点),但关于引用初始化和列表初始化的交互的语言非常模糊。