显式转换函数,直接初始化和转换构造函数 [英] Explicit conversion functions, direct-initialization, and converting constructors

查看:160
本文介绍了显式转换函数,直接初始化和转换构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

后标准草案n3376以使用显式转换函数为用户定义类型为例(12.3.2:2):

  class Y {}; 
struct Z {
显式运算符Y()const;
};
void h(Z z){
Y y1(z); // OK:direct-initialization
}

Per 12.3.2:2,转换函数仅被视为用于直接初始化的用户定义转换;然而,这似乎允许:

  struct Y {Y(int); }; 
struct Z {
显式运算符int()const;
};
void h(Z z){
Y y1(z); // direct-initialization
}

这似乎与标准的意图相冲突,并且确实被gcc-4.7.1拒绝:

  source.cpp:在函数'void h(Z)':
source.cpp:4:9:错误:没有匹配函数调用'Y :: Y(Z&)'
source.cpp:4:9:note:candidate are:
source.cpp:1:12:note:Y :: Y(int)
source.cpp:1:12:注意:参数1从'Z'到'int'没有已知的转换
source .cpp:1:8:注意:constexpr Y :: Y(const Y&)
source.cpp:1:8:注意:参数1没有从'Z'到'const Y&'$的已知转换b $ b source.cpp:1:8:note:constexpr Y :: Y(Y&&)
source.cpp:1:8:注意: g>&'

gcc是否正确拒绝 / code>到 Y 通过 int ,或者标准确实允许这种用法吗?



我考虑了上述直接初始化的上下文;根据8.5:16中的直接初始化到类类型的定义,以初始化器表达式作为其参数来调用构造函数,因此通过隐式转换序列(13.3.3.1)将其转换为参数类型。由于隐式转换序列是隐式转换(4:3),因此模型复制初始化(8.5:14)而不是直接初始化,12.3.2:2中的语言必须是整个表达式。



请注意,这并不违反12.3:4(多个用户定义的转化);同样的编译器对与删除显式(如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中的运算符Y 运算符int 之间的区别,但是有第三种情况,我仍然关心:

  struct X {}; 
struct Y {Y(const X&); };
struct Z {
显式运算符X()const;
};
void h(Z z){
Y y1(z); //通过类类型的直接初始化X
}

code> Y 的构造函数的单个 const X& 参数绑定到 X code>在直接初始化上下文中根据13.3.1.4:1进行, T X S Z 。我认为这个子句是不正确的,应该改为:


13.3.1.4通过用户定义转换的复制初始化[over.match。复制]



1 - [...]初始化一个临时变量绑定到构造函数的第一个参数
, > cv cc的对象的直接初始化上下文中使用单个参数调用的第一个参数调用 cv code> T
,还会考虑显式转换函数。 [...]


为了避免混淆,我认为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)的用户定义的转换em>。 [...]


对上述任何评论?

解决方案

如Luc Danton的回答所述,隐式转换是根据拷贝初始化来定义的。然后,如果我们看看13.3.1.4:1 [Copy-通过用户定义的转换初始化类]:


初始化表达式是类类型cv S,
S 的非显式转换函数及其基类都是
考虑的。初始化一个临时绑定到一个构造函数的第一个
参数,该参数可能引用
cv-qualified T作为第一个参数,在
中用一个参数调用上下文直接初始化,显式转换函数
也被考虑。那些不隐藏在S中并产生$ c $ b类型的cv非限定版本是与T类型相同的或是其
派生类是候选函数。返回引用X的转换函数
根据
类型的引用类型X返回l值或x值,因此被认为在选择候选函数的过程中产生
X 。


如果我正确理解这一点,第一个工作原因是转换函数产生 Y 并且因此是引用中第二个强调部分所指出的候选函数,但是在第二种情况下,候选函数集是空的,因为没有转换函数到 Y ,并且没有第一个强调部分指出的非显式转换函数。



关于第三种情况: p>

找到缺陷报告1087 ,似乎清楚的是,当你提到的直接初始化cv2 T的对象时,意图是允许,复制,移动和模板构造函数。 如果你读了13.3.1.4的第一个段落,它说假设cv1 T是
正在初始化的对象的类型,T类类型
,所以我认为这意味着你提到的类型cv2 T的对象的
然而,由于瑕疵报告而导致的变更已导致措辞变得模糊,不能涵盖您建议的第三种情况。


Post-standard draft n3376 has as an example (12.3.2:2) of the use of an explicit conversion function to a user-defined type:

class Y { };
struct Z {
  explicit operator Y() const;
};
void h(Z z) {
  Y y1(z); // OK: direct-initialization
}

Per 12.3.2:2, an explicit conversion function is "only considered as a user-defined conversion for direct-initialization"; however, that would appear to permit:

struct Y { Y(int); };
struct Z {
  explicit operator int() const;
};
void h(Z z) {
  Y y1(z); // direct-initialization
}

which appears to conflict with the intent of the standard, and indeed is rejected by 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&&'

Is gcc correct to reject the conversion from Z to Y via int, or does the standard indeed permit this usage?

I considered the context of the mentioned direct-initialization; per the definition of direct-initialization to class type in 8.5:16, a constructor is called with the initializer expression as its arguments, which therefore are converted to the parameter type by an implicit conversion sequence (13.3.3.1). Since an implicit conversion sequence is an implicit conversion (4:3), and thus models copy-initialization (8.5:14) and not direct-initialization, the language in 12.3.2:2 must be referring to the expression as a whole.

Note also that this isn't a violation of 12.3:4 (multiple user-defined conversions); the same compiler is happy with the same code with explicit removed (as are Clang and Comeau):

struct Y { Y(int); };
struct Z { operator int(); };
void h(Z z) {
  Y y1(z); // direct-initialization
}


I think Jesse Good has identified the distinction between the operator Y and operator int cases in 13.3.1.4:1, but there's a third case that I'm still concerned by:

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
}

The initialization of the temporary X to be bound to the single const X & parameter of the constructor of Y proceeds in a direct-initialization context per 13.3.1.4:1, with T as X and S as Z. I think this clause is incorrect and should read:

13.3.1.4 Copy-initialization of class by user-defined conversion [over.match.copy]

1 - [...] When initializing a temporary to be bound to the first parameter of a constructor that takes a reference to possibly cv-qualified T as its first argument, called with a single argument in the context of direct-initialization of an object of type "cv2 T", explicit conversion functions are also considered. [...]

For the avoidance of confusion, I think 12.3.2:2 should also be amended:

12.3.2 Conversion functions [class.conv.fct]

2 - A conversion function may be explicit (7.1.2), in which case it is only considered as a user-defined conversion for direct-initialization (8.5) in certain contexts (13.3.1.4, 13.3.1.5, 13.3.1.6). [...]

Any comments on the above?

解决方案

As noted in Luc Danton's answer, implicit conversion is defined in terms of copy initialization. Then, if we look at 13.3.1.4:1[Copy-initialization of class by user-defined conversion]:

When the type of the initializer expression is a class type "cv S", the non-explicit conversion functions of S and its base classes are considered. When initializing a temporary to be bound to the first parameter of a constructor that takes a reference to possibly cv-qualified T as its first argument, called with a single argument in the context of direct-initialization, explicit conversion functions are also considered. Those that are not hidden within S and yield a type whose cv-unqualified version is the same type as T or is a derived class thereof are candidate functions. Conversion functions that return "reference to X" return lvalues or xvalues, depending on the type of reference, of type X and are therefore considered to yield X for this process of selecting candidate functions.

If I understand this correctly, the first one works because the conversion function yields a Y and is therefore a candidate function as noted by the second emphasized part in the quote, however, in your second case, the set of candidate functions is empty because there is no conversion function to Y and no non-explicit conversion functions as noted by the first emphasized part.

Concerning the third case:

After finding defect report 1087, it seems clear that the intention was to allow, copy, move and template constructors when direct-initializing an object of cv2 T as you mention. If you read the first passage of 13.3.1.4, it says Assuming that "cv1 T" is the type of the object being initialized, with T a class type, so I think that implies of an object of type "cv2 T" that you mention. However, (after reading it over), it seems that the change due to the defect report has caused the wording to become vague and not cover the third case you propose.

这篇关于显式转换函数,直接初始化和转换构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆