#include <iostream>
struct U
{
template<typename T>
operator T();
};
template<unsigned I>
struct X : X<I - 1> {};
template<>
struct X<0> {};
template<typename T>
constexpr auto f(X<4>) -> decltype(T(U{}, U{}, U{}, U{}), 0u) { return 4u; }
template<typename T>
constexpr auto f(X<3>) -> decltype(T(U{}, U{}, U{}), 0u) { return 3u; }
template<typename T>
constexpr auto f(X<2>) -> decltype(T(U{}, U{}), 0u) { return 2u; }
template<typename T>
constexpr auto f(X<1>) -> decltype(T(U{}), 0u) { return 1u; }
template<typename T>
constexpr auto f(X<0>) -> decltype(T{}, 0u) { return 0u; }
struct A
{
void* a;
int b;
double c;
};
int main() { std::cout << f<A>(X<4>{}) << std::endl; }以上代码为gcc和clang所接受。然而,gcc给出了预期的输出3,而不是clang给出的意外输出1。
请参阅:https://godbolt.org/z/YKnxWah1a
相关问答:Why does Clang 12 refuse to initialize aggregates in the C++20 way?
在本例中哪个是正确的?
发布于 2021-12-29 17:29:31
U是一种声称可兑换到任何其他类型的类型。“任何其他类型”包括向T模板提供的任何f。
因此,1总是一个潜在的合法超载;SFINAE允许它存在。
A是由3种元素组成的集合。因此,它可以通过包含0到3个元素的初始化程序列表进行初始化。C++20允许聚合使用构造函数语法进行聚合初始化。
因此,3、2、1和0都是潜在的合法超载;SFINAE允许它们存在。在预C++20规则下,所有这些规则都不是聚合初始化,因此它们中没有一个是有效的SFINAE重载(保存1,因为U的上述属性而工作)。
到目前为止,Clang还没有实现C++20的聚合初始化规则,因此就Clang而言,唯一可用的重载是X<1>。
对于功能更全面的C++编译器,问题是:根据C++的过载解析规则,哪种重载更好?
好的,所有可用的重载都涉及从参数类型X<4>到它的一个基类的隐式转换。但是,基于基类转换的重载解析使更接近继承图中参数类型的基类具有优先权。因此,X<3>优先于X<2>,尽管两者都是可用的。
因此,根据C++20的规则和过载解决方案,3应该是正确的答案。
https://stackoverflow.com/questions/70522630
复制相似问题