5

考虑以下代码片段:

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,两个编译器都拒绝该代码。C++17 中新的纯右值具体化规则可能会影响此处的行为。

godbolt.org 上的实时示例


这里的正确行为是什么?

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

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

4

1 回答 1

4

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

于 2018-06-12T16:49:41.263 回答