被删除的定义会影响重载解析吗?

编程语言 2026-07-09

请看下面的示例(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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章