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

Vit*_*meo 5 c++ conversion-operator language-lawyer copy-elision c++17

请考虑以下代码段:

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{}};
}
Run Code Online (Sandbox Code Playgroud)

编译时-std=c++17:

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

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

编译时-std=c++11,两个编译器都拒绝代码.C++ 17中新的prvalue实现规则可能会影响此处的行为.

godbolt.org上的实例


这里的正确行为是什么?

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

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

Bar*_*rry 4

这是CWG 2327

考虑一个例子:

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

Dog d;
Cat c(d);
Run Code Online (Sandbox Code Playgroud)

这转到 11.6 [dcl.init] 项目符号 17.6.2:

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

重载决策选择 Cat 的移动构造函数。根据 11.6.3 [dcl.init.ref] 项目符号 5.2.1.2,初始化构造函数的 Cat&& 参数会产生临时结果。这排除了这种情况下复制省略的可能性。

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

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

根据标准,gcc 是正确的。