被删除的定义会影响重载解析吗?
请看下面的示例(live):
#include <optional>
#include <print>
struct DBEntry {
template <class T>
operator T() const // #1
{
std::println("operator T");
return {};
}
template <class T>
operator std::optional<T>() const // #2
{
std::println("operator std::optional<T>");
return {};
}
};
int main()
{
DBEntry entry;
std::optional<std::string> s = entry; // expected to call #2
}
在MSVC上,调用了 #2,正如我所预期的。在Clang和 GCC上,调用了 #1。为了了解Clang与 GCC的表现,我把 #1 的定义改成了 = delete:
template <class T>
operator T() const = delete; // #1
它们并没有给我想要的错误信息,反而突然选择了 #2。
定义 = delete 会影响重载解析吗?
解决方案
当#1未被删除时,存在三个候选项(std::optional<std::string> 的转换构造函数以及两个转换运算符):
template<class T> // [T = std::string]
struct optional {
template<class U> // [U = DBEntry&]
requires(std::is_constructible_v<T, U>)
explicit(false) optional(U&&); // Converting constructor
};
struct DBEntry {
template<class T> // [T = std::optional<std::string>]
operator T() const; // Conversion operator #1
template<class T> // [T = std::string]
operator std::optional<T>() const; // Conversion operator #2
};
转换构造函数更优,因为它接收一个 DBEntry&,但两个转换运算符的隐式对象参数类型都是 const DBEntry,而加入的 const 成为打破平局的决定性因素†。
当#1被删除时,optional 的转换构造函数不再可行,因为它的约束不成立。这在某种程度上是正交的:被删除的转换运算符被选中(因此在 is_constructible_v 时的重载解析并未受到影响),这也是我们能够观察到它被删除的根本原因。候选项减少一个,因此选择#2,因为它比#1更具特化性。
† 注:如果你将转换运算符设为非 const,或拥有 const DBEntry entry;,则两者都不会被优先考虑,转换将变得模棱两可。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。