隐式转换运算符 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
编译时:
clang++
(trunk)成功编译代码;
g++
(trunk) 无法编译代码 - 它实例化 foo(const T&)
.
使用 -std=c++11
编译时,两个编译器都拒绝该代码。 C++17 中新的 prvalue 物化规则可能会影响此处的行为。
这里的正确行为是什么?
标准是否保证 foo::foo(const T&)
将被(或不被)实例化?
标准是否保证 隐式转换运算符 优先于 foo::foo(const T&)
的调用,无论它是否被实例化?
这是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 是正确的。
考虑以下代码片段:
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&)
的调用,无论它是否被实例化?
这是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 是正确的。