提问人:Erik Nouroyan 提问时间:7/1/2023 更新时间:7/1/2023 访问量:72
为什么编译器在删除模板时不选择其他重载?
Why doesn't the compiler choose the other overload when I remove template?
问:
我正在尝试编写 C++ 代码来了解某个类是否具有默认构造函数(没有 C++20 概念)。以下代码工作正常。
#include <iostream>
class C {
public:
C() = delete;
};
template<typename T>
struct HasDefaultConstructor {
template<typename U>
static auto test(int) -> decltype(U(), std::true_type{});
template<typename U>
static auto test(...) -> std::false_type;
static constexpr bool value = decltype(test<T>(0))::value;
};
int main() {
bool a = HasDefaultConstructor<C>::value;
std::cout << std::boolalpha << a << std::endl;
return 0;
}
但是,当我将测试函数更改为以下函数并使用时,会出现编译错误。decltype(test(0))::value
static auto test(int) -> decltype(T(), std::true_type{});
static auto test(...) -> std::false_type;
错误说这是一个已删除的函数。我想知道为什么编译器在它是模板化版本时不抱怨,为什么当版本编译失败时它不选择()的其他版本?您能解释一下SFINAE在这里是如何工作的吗?C::C()
test
test(...)
int
答:
3赞
JeJo
7/1/2023
#1
我想知道为什么编译器在它是模板化版本时不抱怨,为什么当版本编译失败时它不选择()的其他版本?
test
test(...)
int
为了使用 SFINAE,template 参数必须来自 SFINAE 的函数模板重载。
此规则适用于函数模板的重载解析:当将显式指定或推导的类型替换为模板参数失败时,将从重载集中丢弃专用化,而不会导致编译错误。
在第二种情况下,它们不再是函数模板,而是使用类模板参数来定义其尾随返回类型的成员。
template<typename T>
struct HasDefaultConstructor
{
// following are not function templates!!
static auto test(int) -> decltype(T(), std::true_type{});
static auto test(...) -> std::false_type;
// ....
};
即您直接使用类模板中的模板参数类型,而不是函数的模板参数(即 )。这会更改行为,因为即使未计算表达式,也会在替换阶段解析类型。T
U
T
T()
问题在于,失败的替换发生在函数模板的返回类型中,这不是 SFINAE 直接上下文的一部分。因此,编译器会报告错误,而不是应用 SFINAE 并选择第二个重载。
评论