9阅网

您现在的位置是:首页 > 知识 > 正文

知识

c++ - 关于函数模板实例化的sfinae。

admin2022-11-07知识18

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

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++中,你必须对函数本身进行约束,以使检测有效。

演示 (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++在 类型 而函数的主体不被认为是函数类型的一部分。