转换运算符与删除的构造函数
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);
}
这里,Y
的构造函数采用 X
被显式删除,而 X
有一个到 Y
的转换运算符。在这直接矛盾的两个中,似乎=delete
总是赢;我测试了一些最新版本的 GCC、Clang 和 VC++。
问题:是 "right" 行为吗?我认为转换构造函数和转换运算符之间没有特别的优先级,所以上面的代码应该会产生重载解析歧义错误。但事实并非如此。它抱怨已删除函数的使用。是因为有保证的复制省略吗?
我用谷歌搜索并找到 Conversion constructor vs. conversion operator: precedence。在那个问题中,选择了转换运算符,因为转换构造函数中存在 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
if the initialization is direct-initialization, or if it is copy-initialization where the
cv-unqualified version of the source type is the same class as, or a derived class of, the class of the
destination, constructors are considered.
那么标准告诉我们(11.6.16
)
The initialization that occurs in the forms [...] as well as in new
expressions (8.5.2.4), static_cast
expressions (8.5.1.9), functional notation type conversions (8.5.1.3), mem-initializers (15.6.2), and the braced-init-list form of a condition is called direct-initialization.
您的示例通过 static_cast
初始化一个临时变量,因此由于直接初始化,编译器只允许使用构造函数,因此您会得到一个错误。
The question: is it the "right" behavior? I thought there is no particular precedence between conversion constructor and conversion operator [...]
不太正确。您正在按原样查看代码:
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;
Then 两者同样可行(因此不明确)。是的,你真的希望它是模棱两可的。 = delete
不从候选集中移除!
将构造函数从 Y(X&)
更改为 Y(X const&)
将优先使用转换函数。
Yes, one may call this silly, but indeed there are built-in types that behave just like that: substitute X <- int&
, Y <- int&&
是的,但是在原来的例子中,X
和Y
是不同的类型。在这里,它们代表同一类型的不同值类别。这个新示例工作或不工作的原因完全不同:
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)
对引用兼容类型的引用绑定与转换优先级不是同一类问题。
请看下面代码:
struct X;
struct Y {
Y() {}
Y(X&) = delete;
};
struct X {
X() {}
operator Y() {
return{};
}
};
int main() {
X x;
static_cast<Y>(x);
}
这里,Y
的构造函数采用 X
被显式删除,而 X
有一个到 Y
的转换运算符。在这直接矛盾的两个中,似乎=delete
总是赢;我测试了一些最新版本的 GCC、Clang 和 VC++。
问题:是 "right" 行为吗?我认为转换构造函数和转换运算符之间没有特别的优先级,所以上面的代码应该会产生重载解析歧义错误。但事实并非如此。它抱怨已删除函数的使用。是因为有保证的复制省略吗?
我用谷歌搜索并找到 Conversion constructor vs. conversion operator: precedence。在那个问题中,选择了转换运算符,因为转换构造函数中存在 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
if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered.
那么标准告诉我们(11.6.16
)
The initialization that occurs in the forms [...] as well as in
new
expressions (8.5.2.4),static_cast
expressions (8.5.1.9), functional notation type conversions (8.5.1.3), mem-initializers (15.6.2), and the braced-init-list form of a condition is called direct-initialization.
您的示例通过 static_cast
初始化一个临时变量,因此由于直接初始化,编译器只允许使用构造函数,因此您会得到一个错误。
The question: is it the "right" behavior? I thought there is no particular precedence between conversion constructor and conversion operator [...]
不太正确。您正在按原样查看代码:
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;
Then 两者同样可行(因此不明确)。是的,你真的希望它是模棱两可的。 = delete
不从候选集中移除!
将构造函数从 Y(X&)
更改为 Y(X const&)
将优先使用转换函数。
Yes, one may call this silly, but indeed there are built-in types that behave just like that: substitute
X <- int&
,Y <- int&&
是的,但是在原来的例子中,X
和Y
是不同的类型。在这里,它们代表同一类型的不同值类别。这个新示例工作或不工作的原因完全不同:
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)
对引用兼容类型的引用绑定与转换优先级不是同一类问题。