代码之家  ›  专栏  ›  技术社区  ›  Vittorio Romeo

隐式转换运算符与模板构造函数-应优先考虑谁?

  •  5
  • Vittorio Romeo  · 技术社区  · 7 年前

    请考虑以下代码段:

    template <typename>
    struct dependent_false { static constexpr auto value = false; };
    
    struct foo
    {
        foo() { }
    
        template <typename T>
        foo(const T&) { static_assert(dependent_false<T>::value, ""); }
    };
    
    struct proxy
    {
        operator foo() { return foo{};  }
    };
    
    int main()
    {
        (void) foo{proxy{}};
    }
    

    编译时使用 -std=c++17 :

    • clang++ (trunk)成功编译代码;

    • g++ (trunk)无法编译代码-它实例化 foo(const T&) .

    编译时使用 -std=c++11 ,两个编译器都拒绝代码。新的 PR值 C++ 17中的实体化规则可能会影响这里的行为。

    live example on godbolt.org


    这里的正确行为是什么?

    • 标准是否保证 foo::foo(const T&) 是否将被实例化?

    • 标准是否保证 隐式转换运算符 将优先于调用 foo::foo(常量) ,不管它是否被实例化?

    1 回复  |  直到 7 年前
        1
  •  4
  •   Barry    7 年前

    这是 CWG 2327 :

    举个例子:

    struct Cat {};
    struct Dog { operator Cat(); };
    
    Dog d;
    Cat c(d);
    

    这是11.6[dcl.init]第17.6.2条:

    否则,如果初始化是直接初始化,或者如果是复制初始化,而源类型的cv非限定版本与目标类型的类或其派生类是同一类,则将考虑构造函数。枚举适用的构造函数(16.3.1.3[over.match.ctor]),并通过重载解析(16.3[over.match])选择最佳的构造函数。调用所选的构造函数以初始化对象,并将初始值设定项表达式或表达式列表作为其参数。如果没有应用构造函数,或者重载解析不明确,则初始化格式错误。

    重载解析选择cat的move构造函数。根据11.6.3[dcl.init.ref]项目符号5.2.1.2,初始化构造函数的cat&参数将导致临时的。这就排除了在本案中删除副本的可能性。

    这似乎是一个疏忽的措辞变化,以保证副本删除。在这种情况下,我们应该同时考虑构造函数和转换函数,就像复制初始化一样,但是我们需要确保这不会引入任何新的问题或歧义。

    我相信clang实现了这个隐含的更改(因此认为转换函数更匹配),而gcc没有(因此从来没有真正考虑转换函数)。

    根据标准,GCC是正确的。