在后标准草案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节的规定,显式转换函数仅被视为直接初始化的用户定义转换;然而,这似乎允许以下情况:
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是否正确地拒绝了通过int将Z转换为Y的转换,还是标准确实允许这种用法?我考虑了所提到的直接初始化的上下文。根据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
}
对于Y
的构造函数的单个const X&
参数绑定到临时X
的初始化,按照13.3.1.4:1进行直接初始化上下文,其中T
为X
,而S
为Z
。我认为此条款是错误的,应该更改为:
13.3.1.4类通过用户定义的转换进行复制初始化[over.match.copy]
1 - [...]当初始化一个临时变量以绑定到作为其第一个参数的可能带有cv-qualified
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)的用户定义转换。[...]
对以上内容有什么评论吗?