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

Implicit conversion operator vs template constructor - who should be prioritized?

考虑以下代码片段:

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编译时:

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

live example on godbolt.org


这里的正确行为是什么?

这是CWG 2327:

Consider an example like:

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

Dog d;
Cat c(d);

This goes to 11.6 [dcl.init] bullet 17.6.2:

Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (16.3.1.3 [over.match.ctor]), and the best one is chosen through overload resolution (16.3 [over.match]). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

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

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

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

根据标准,gcc 是正确的。