请考虑以下代码段:
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中新的 prvalue 具体化规则可能会影响此处的行为。
这里的正确行为是什么?
标准是否保证将foo::foo(const T&)
实例化(或不实例化)?
标准是否保证隐式转换运算符优先于foo::foo(const T&)
的调用,无论它是否被实例化?
答案 0 :(得分:4)
这是CWG 2327:
考虑一个例子:
struct Cat {}; struct Dog { operator Cat(); }; Dog d; Cat c(d);
这是11.6 [dcl.init] bullet 17.6.2:
否则,如果初始化是直接初始化,或者它是复制初始化,其中源类型的cv-nonqualified版本与目标类相同的类,或派生类,构造函数是考虑。枚举适用的构造函数(16.3.1.3 [over.match.ctor]),并通过重载决策(16.3 [over.match])选择最佳构造函数。调用所选的构造函数来初始化对象,初始化表达式或表达式列表作为其参数。如果没有构造函数适用,或者重载决策不明确,则初始化是不正确的。
重载分辨率选择Cat的移动构造函数。初始化Cat&amp;&amp;根据11.6.3 [dcl.init.ref]子弹5.2.1.2,构造函数的参数产生一个临时的。这排除了这种情况下复制省略的可能性。
这似乎是对保证副本省略的措辞变更的疏忽。在这种情况下,我们应该同时考虑构造函数和转换函数,就像复制初始化一样,但是我们需要确保不会引入任何新的问题或含糊之处。
我相信clang实现了这个隐含的变化(因此认为转换函数更好地匹配)并且gcc没有(因此从未真正考虑转换函数)。
根据标准,gcc是正确的。