【问题标题】:Implicit conversion operator vs template constructor - who should be prioritized?隐式转换运算符与模板构造函数 - 应该优先考虑谁?
【发布时间】:2023-03-10 15:23:01
【问题描述】:

考虑以下代码sn-p:

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++(主干)无法编译代码 - 它实例化了foo(const T&amp;)

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

live example on godbolt.org


这里的正确行为是什么?

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

  • 标准是否保证隐式转换运算符将优先于foo::foo(const T&amp;)的调用,无论它是否被实例化?

【问题讨论】:

    标签: c++ language-lawyer c++17 copy-elision conversion-operator


    【解决方案1】:

    这是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 是正确的。

    【讨论】:

      猜你喜欢
      • 2010-11-25
      • 2020-12-14
      • 2012-09-02
      • 2021-12-30
      • 2017-07-04
      • 2014-07-28
      • 1970-01-01
      • 2012-05-04
      • 2021-01-01
      相关资源
      最近更新 更多