繁体   English   中英

转换运算符vs删除的构造函数

[英]Conversion operator vs deleted constructor

请参见以下代码:

struct X;

struct Y {
  Y() {}
  Y(X&) = delete;
};

struct X {
  X() {}
  operator Y() {
    return{};
  }
};

int main() {
  X x;
  static_cast<Y>(x);
}

在这里,显式删除了使用XY的构造函数,而X将转换运算符转换为Y 在这两个直接矛盾的地方,似乎=delete总是赢; 我在GCC,Clang和VC ++的某些最新版本上进行了测试。

问题:这是“正确的”行为吗? 我认为转换构造函数和转换运算符之间没有特殊的优先级,因此上面的代码应产生重载解析度歧义错误。 但事实并非如此。 它抱怨删除功能的使用。 是因为可以保证复制保留吗?

我用谷歌搜索,发现转换构造函数与转换运算符:priority 在该问题中,选择了转换运算符,因为由于转换构造const中存在const ,因此它更匹配。 但是,在我的情况下,将Y(X&)替换为Y(X const&)没有改变。


实际上,我想要的情况如下:

X x;
Y y1(x);                  // Error
Y y2 = static_cast<Y>(x); // OK

是的,这可能很愚蠢,但是确实有一些内置类型的行为就像这样: X <- int&Y <- int&& 无法创建完全模仿内置引用类型的用户定义类型似乎是当前C ++迫切需要的部分...

从标准11.6.17.6.2

如果初始化是直接初始化,或者如果复制是初始化,则源类型的cv不合格版本与目标的类相同,或者是该类的派生类,则考虑构造函数。

然后标准告诉我们( 11.6.16

各种形式的初始化以及new表达式(8.5.2.4), static_cast表达式(8.5.1.9),功能符号类型转换(8.5.1.3),mem初始化器(15.6.2) ,条件的括号初始列表形式称为直接初始化。

您的示例通过static_cast初始化了一个临时项,因此由于直接初始化,编译器只允许使用构造函数,因此会出现错误。

问题:这是“正确的”行为吗? 我以为转换构造函数和转换运算符之间没有特别的优先级[...]

那不是很正确。 您正在按原样查看代码:

struct Y {
  Y() {}
  Y(X&) = delete;
};

但实际上还有更多的东西。 对于编译器, Y看起来像:

struct Y {
  Y() {}
  Y(X&) = delete;

  Y(Y&&) = default;
  Y(Y const&) = default;
};

这里的选择不在Y(X&)X::operator Y() 选择主要在Y(X&)Y(Y&&) 而且前者比后者更好(不管,正如您在问题中提到的那样,它都是X&X const&作为参数)。 但是它已被删除,因此转换格式错误。


如果我们是复制初始化而不是直接初始化:

Y y = x;

那么两者将是同样可行的(因此模棱两可)。 是的,您真的希望这个模棱两可。 = delete不会从候选集中删除!

将构造函数从Y(X&)更改为Y(X const&)将具有首选的转换功能。


是的,这可能很愚蠢,但是确实有一些内置类型的行为就像这样: X <- int&Y <- int&&

是的,但是在原始示例中, XY是不同的类型 在这里,它们代表相同类型的不同值类别。 这个新示例起作用或不起作用的原因完全不同:

X x;
Y y1(x);                  // Error
Y y2 = static_cast<Y>(x); // OK

是真的:

int& x = ...;
int&& y(x);                       // error, can't bind rvalue reference to lvalue
int&& y2 = static_cast<int&&>(x); // ok. this is exactly std::move(x)

与引用兼容类型的引用绑定与转换优先级的问题不同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM