为了账号安全,请及时绑定邮箱和手机立即绑定

SFINAE适用的C ++ 11标准中提到的“即时上下文”到底是什么?

SFINAE适用的C ++ 11标准中提到的“即时上下文”到底是什么?

C++
函数式编程 2019-09-26 10:30:46
C ++ 11标准的14.8.2 / 8段规定了替换失败应或不应该导致“硬”编译错误(从而导致编译失败)或“软”错误的条件,使编译器从一组候选集中丢弃模板以进行重载解决(而不会使编译失败并启用众所周知的SFINAE惯用语):如果替换导致无效的类型或表达式,则类型推导将失败。无效的类型或表达式是使用替换参数编写的格式或表达式。[注意:访问检查是替代过程的一部分。— —注释[end note] 只有在函数类型及其模板参数类型的直接上下文中无效的类型和表达式才可能导致推论失败。[...]在整个C ++ 11标准中,“ 即时上下文 ”一词仅出现8次,并且每次其后跟随(或作为其一部分)以下(非规范)文本的实例:[注:对替换类型和表达式的求值可能会导致副作用,例如实例化类模板专业化和/或函数模板专业化,生成隐式定义的函数等。此类副作用不在“立即”中。上下文”,并可能导致程序格式错误。—尾注]该注释对即时上下文的含义给出了(不是很慷慨的)提示,但至少对我而言,这通常不足以确定独占是否会导致“硬”编译错误。题:您能否提供说明,决策过程和/或一些具体示例,以帮助弄清楚在什么情况下函数类型及其模板参数类型的“ 立即上下文 ”中发生或不发生替代错误?
查看完整描述

2 回答

?
白板的微信

TA贡献1883条经验 获得超3个赞

直接背景基本上是什么,你在模板声明本身看到的。除此之外的一切都是硬错误。硬错误示例:

#include <type_traits>template<class T>struct trait{ using type = typename T::type; };template<class T, class U = typename trait<T>::type>void f(int);void f(...);template<class T, class U = typename T::type>void g(int);void g(...);template<class>struct dependent_false : std::false_type{};template<class T>struct X{
    static_assert(dependent_false<T>(), "...");
    using type = void;};int main(){
    f<int>(0);
    g<X<int>>(0);}

现场版。


查看完整回答
反对 回复 2019-09-26
?
哈士奇WWW

TA贡献1799条经验 获得超6个赞

如果您考虑了确定模板参数替换结果所需的所有模板和隐式定义的函数,并假设它们是在替换开始之前首先生成的,则在第一步中发生的任何错误都不会立即出现,并导致硬错误。

如果所有这些实例化和隐式定义(可能包括将函数定义为已删除)都可以正确执行,那么替换期间(即在引用函数模板的实例化模板和隐式定义函数时)会发生任何其他“错误”。签名)不是错误,但会导致推理失败。

因此,给定这样的功能模板:

template<typename T>voidfunc(typename T::type* arg);

以及在其他功能的推论失败时将使用的“后备”:

template<typename>voidfunc(...);

和这样的类模板:

template<typename T>
  struct A  {
    typedef T* type;
  };

调用func<A<int&>>(nullptr)将替代A<int&>T并且为了检查是否T::type存在,必须实例化A<int&>。如果我们设想在调用之前放一个显式实例化func<A<int&>(nullptr)

template class A<int&>;

那么这将失败,因为它尝试创建类型int&*并且不允许使用指向引用的指针。我们没有到检查替换是否成功的地步,因为实例化存在一个严重的错误A<int&>

现在,我们有一个明确的专业化A

template<>
  struct A<char>
  {
  };

对的调用func<A<char>>(nullptr)需要实例化A<char>,因此请想象在调用之前程序中某个地方的显式实例化:

template class A<char>;

此实例化还可以,没有任何错误,因此我们继续进行参数替换。work的实例化A<char>,但A<char>::type不存在,但是可以,因为它仅在的声明中引用func,因此只会导致参数推导失败,而...将调用后备功能。

在其他情况下,替换可能导致特殊成员函数被隐式定义(可能已删除),这可能会触发其他实例化或隐式定义。如果在“生成实例和隐式定义”阶段发生错误,那么它们就是错误,但是如果成功但在替换期间发生错误,则函数模板签名中的表达式将变为无效,例如,因为它使用的成员不存在或被隐式定义为删除的东西,这不是错误,只是推论失败。

因此,我使用的思维模型是,替换需要首先执行“准备”步骤才能生成类型和成员,这可能会导致严重错误,但是一旦我们完成了所有必要的生成,任何进一步的无效使用就不会出错。当然,这一切都是将问题从“ 即时上下文是什么意思?” 移开。“在检查此替换之前需要生成哪些类型和成员?” 因此它可能会或可能不会帮助您!


查看完整回答
反对 回复 2019-09-26
  • 2 回答
  • 0 关注
  • 630 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信