我想检查一个函数模板是否可以为给定类型实例化。举个例子,对于模板。
template<typename T> void f() { T{}; }
我想断言 f<int>
是可实例化的,并且 f<S>
不是,其中 S
是某种类型,会导致 f
如果被实例化,编译失败。
struct S { ~S() = delete; };
很明显,如果我知道函数体包含什么, 我可以为每条语句分别写检查。但是,如果函数体本身是未知的,这种方法就行不通了。
看来,我不能使用通常的sfinae方法来做这件事,因为函数体没有被检查出演绎失败。下面是 尝试 来玩的。
确实如此。temp.deduct.8 似乎明确不允许这样做。
只有在函数类型的直接上下文中的无效类型和表达式, 它的模板参数类型, 以及它的显式指定器才会导致演绎失败.
而 "即时上下文 "约束的原因似乎就在下一个要点中。
注意:目的是避免要求实现者处理涉及任意语句的替换失败。
如果我的理解是正确的,做到这一点是不可能的,能不能给我一个解释?何以 这种约束存在吗?I 认为 任意 表情 可以被用户检查是否有替换失败的情况,为什么这对一个实现来说会是太多的事情呢?
如果我理解错了,而且它 是 可能的话,我可以解决这个问题吗?
正如你的问题中所说,SFINAE只发生在 "即时语境 "中,而 "扣款失败 "所包含的情况有哪些呢?[temp.deduct].在你的代码中发生的错误将是错误的,这在标准中是有提到的。
替换成类型和表达式会导致一些影响,比如类模板特殊化和函数模板特殊化的实例化,隐式定义函数的生成等等。这样的效果并不在 "直接上下文 "中,会导致程序的形式不良。
所以,你的程序在要求实例化类型为f的函数模板时,就会出现形式错误。S
.
#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++中,你必须对函数本身进行约束,以使检测有效。
#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++在 类型 而函数的主体不被认为是函数类型的一部分。