后标准草案 n3376 以使用显式转换函数到用户定义类型的示例 (12.3.2:2) 为例:
class Y { };
struct Z {
explicit operator Y() const;
};
void h(Z z) {
Y y1(z); // OK: direct-initialization
}
根据 12.3.2:2,显式转换函数“仅被视为直接初始化的用户定义转换”;但是,这似乎允许:
struct Y { Y(int); };
struct Z {
explicit operator int() const;
};
void h(Z z) {
Y y1(z); // direct-initialization
}
这似乎与标准的意图相冲突,并且确实被 gcc-4.7.1 拒绝:
source.cpp: In function 'void h(Z)':
source.cpp:4:9: error: no matching function for call to 'Y::Y(Z&)'
source.cpp:4:9: note: candidates are:
source.cpp:1:12: note: Y::Y(int)
source.cpp:1:12: note: no known conversion for argument 1 from 'Z' to 'int'
source.cpp:1:8: note: constexpr Y::Y(const Y&)
source.cpp:1:8: note: no known conversion for argument 1 from 'Z' to 'const Y&'
source.cpp:1:8: note: constexpr Y::Y(Y&&)
source.cpp:1:8: note: no known conversion for argument 1 from 'Z' to 'Y&&'
gcc 拒绝从Z
到Y
via的转换是正确的int
,还是标准确实允许这种用法?
我考虑了提到的直接初始化的上下文;根据 8.5:16 中对类类型的直接初始化定义,使用初始化表达式作为其参数调用构造函数,因此通过隐式转换序列 (13.3.3.1) 将其转换为参数类型。由于隐式转换序列是隐式转换 (4:3),因此模型复制初始化 (8.5:14) 而不是直接初始化,因此 12.3.2:2 中的语言必须引用整个表达式。
另请注意,这并不违反 12.3:4(多个用户定义的转换);相同的编译器对删除后的相同代码感到满意explicit
(Clang 和 Comeau 也是如此):
struct Y { Y(int); };
struct Z { operator int(); };
void h(Z z) {
Y y1(z); // direct-initialization
}
我认为 Jesse Good 已经确定了 13.3.1.4:1 中的operator Y
和operator int
案例之间的区别,但我仍然担心第三种情况:
struct X {};
struct Y { Y(const X &); };
struct Z {
explicit operator X() const;
};
void h(Z z) {
Y y1(z); // direct-initialization via class-type X
}
根据13.3.1.4:1,使用as和asX
将绑定到const X &
构造函数的单个参数的临时初始化在直接初始化上下文中进行。我认为该条款不正确,应改为:Y
T
X
S
Z
13.3.1.4 通过用户定义的转换复制初始化类[over.match.copy]
1 - [...] 当初始化一个临时绑定到构造函数的第一个参数时,该构造函数将引用可能cv限定为它的第一个参数,在对象
T
的直接初始化的上下文中使用单个参数调用类型为 "cv2T
"的,还考虑了显式转换函数。[...]
为了避免混淆,我认为 12.3.2:2 也应该修改:
12.3.2 转换函数[class.conv.fct]
2 - 转换函数可能是显式的 (7.1.2),在这种情况下,它仅被视为在某些上下文 (13.3.1.4、13.3.1.5、13.3.1.6 ) 中用于直接初始化 (8.5) 的用户定义转换. [...]
对以上有什么意见吗?