代码之家  ›  专栏  ›  技术社区  ›  user17732522

使用显式默认构造函数从空大括号复制列表初始化

  •  2
  • user17732522  · 技术社区  · 1 年前

    为什么似乎所有的编译器都拒绝以下程序?( https://godbolt.org/z/EK8zW34nY )

    struct A
    {
        explicit A() {}
    };
    
    int main()
    {
        A a = {};
    }
    

    a 应该是 值已初始化 [dcl.init.list]/3.5 根据我所知,这应该反过来选择一个构造函数,就像 默认初始化 默认初始化不排除 explicit 建设者。


    这是在另一个问题中与另一个用户讨论的后续内容:

    有人会申请吗 [over.match.list] ,我认为不应该在这里应用,那么它就会失败,因为“ 在复制列表初始化中,如果选择显式构造函数,则初始化格式不正确。 ".

    将程序修改为

    struct A
    {
        explicit A() {}
    
        template<typename = void>
        A() {}
    };
    
    int main()
    {
        A a = {};
    }
    

    那么GCC和Clang仍然会拒绝它,并显示错误消息,暗示他们是这样认为的,而EDG和MSVC会接受,选择模板构造函数,就像有“ 复制默认初始化 “它完全排除了显式构造函数( https://godbolt.org/z/xbzjhYYhM )

    2 回复  |  直到 1 年前
        1
  •  3
  •   NathanOliver    1 年前

    当你这样做的时候

    A a = {};
    

    您正在使用 复制列表初始化 这带来了 [over.match.ctor] 其中指出:

    当类类型的对象被直接初始化、从相同或派生类类型的表达式([dcl.init])进行复制初始化或默认初始化时,重载解析会选择构造函数。对于直接初始化或默认初始化(包括复制列表初始化上下文中的默认初始化),候选函数都是被初始化对象类的构造函数。 否则,候选函数是该类的所有转换构造函数([class.conv.ctor])。参数列表是初始化器的表达式列表或赋值表达式。对于复制列表初始化上下文中的默认初始化,如果选择显式构造函数,则初始化格式不正确。

    由于对象不是从相同或派生类型的表达式初始化的,因此后半部分适用,最后一句的最后一部分使代码格式不正确。


    这是有道理的,因为 {} 不是 A ,它是一个隐式对象创建者,您不能从具有显式默认构造函数的类型中创建隐式对象。

        2
  •  2
  •   user17732522    1 年前

    事实证明,这是一个阅读理解的问题。结束 [over.match.ctor] 之后 CWG 2856 说:

    对于复制列表初始化上下文中的默认初始化,如果选择显式构造函数,则初始化格式不正确。

    因此,GCC和Clang的行为是正确的,即使应用了[over.match.ctor]而不是[over.mamatch.list]。

    MSVC和EDG可能还没有实施缺陷报告。在此缺陷报告之前,排除了[over.match.ctor]的措辞 explicit 用于在复制列表初始化上下文中直接初始化的构造函数。