函数模板实例化的 sfinae



我想检查是否可以为给定类型实例化函数模板。例如,对于模板:

template<typename T> void f() { T{}; }

我想断言f<int>是可实例化的,而f<S>不是,其中S是某种类型,如果实例化会导致f的定义编译失败,例如:

struct S { ~S() = delete; };

显然,如果我知道函数体包含什么,我可以为每个语句编写单独的检查。但是,如果身体本身未知,这种方法将不起作用。

看来我不能使用通常的 sfinae 方法来做到这一点,因为没有检查函数体的演绎失败。这是玩的尝试。

事实上,temp.deduct.8 似乎明确禁止这样做:

只有函数类型、其模板参数类型及其显式说明符的直接上下文中的无效类型和表达式才可能导致推导失败。

"即时上下文"约束的原因似乎在下一个要点中:

注意:目的是避免要求实现处理涉及任意语句的替换失败。

如果我理解正确,并且这样做是不可能的,我能解释为什么存在这个约束吗?我认为用户可以检查任意表达式的替换失败,那么为什么这对于实现来说太过分了?

如果我误解了这一点,这是可能的,我能解决这个问题吗?

正如你的问题所说,SFINAE 只发生在"直接上下文"中,"演绎失败"所包含的情况列在 [temp.deduc]. 代码中发生的错误将是格式不正确的,这在标准中提到:

对类型和表达式的替换可能会导致诸如类模板专用化和/或函数模板专用化的实例化、隐式定义函数的生成等效果。这种影响不在"直接上下文"中,可能导致程序格式不正确。

因此,当需要为类型S实例化函数模板 f 时,您的程序将格式不正确。

溶液:

#include <iostream>
#include <type_traits>
template<typename T> void f() { T a{}; }
struct S { ~S() = delete; };
struct Test{};
template<typename T,typename U = void>
struct is_sutable_type_for_function:std::false_type{};
template<typename T>
struct is_sutable_type_for_function<T,std::void_t<decltype(T{}.~T())>>:std::true_type{};
int main()
{
std::cout<< is_sutable_type_for_function<S>::value<<"n";
std::cout<< is_sutable_type_for_function<Test>::value<<"n";
}

现在,代码将用于类型T是否可以用于函数模板f

在C++中,您必须对函数本身进行约束才能使检测正常工作:

演示 (C++20(

#include <type_traits>
template<typename T> void f() requires std::is_constructible_v<T> { T{}; }
template<class T, class = void>
struct can_call_f : std::false_type{};
template<class T>
struct can_call_f<T, std::void_t<decltype(f<T>())>>: std::true_type{};
template<class T>
constexpr bool can_call_f_v = can_call_f<T>::value;
struct S { ~S() = delete; };
int main()
{
static_assert(can_call_f_v<int>);
static_assert(!can_call_f_v<S>);
}

回想一下,C++适用于类型,函数的主体不被视为函数类型的一部分。

最新更新