简体   繁体   English

哪些规范规则管理和允许 SFINAE?

[英]What normative rules govern and allow SFINAE?

SFINAE, "Substitutation Failure Is Not An Error", is a well-known rule/technique applied during overload resolution of function templates (see eg SFINAE @ cppreference ). SFINAE,“替换失败不是错误”,是在 function 模板的重载解析期间应用的众所周知的规则/技术(参见例如SFINAE @cppreference )。 However, it is never referred to by that name in the C++ standard;但是,在 C++ 标准中从未使用该名称引用它; the only reference I can find is that of an implementation-dependent macro from <version> , as specified in [version.syn]/2我能找到的唯一参考是来自<version>的依赖于实现的宏,如[version.syn]/2中所指定

#define __cpp_lib_result_of_sfinae 201210L // also in <functional>, <type_traits>

What is(/are) the normative reference(s) for this rule/technique?该规则/技术的规范参考是什么?

Let's start from an unqualified function call ( f(1) below), particularly in an example constructed such that the postfix-expression of the function call names two function templates, one of which we expect to be rejected by SFINAE.让我们从一个不合格的 function 调用(下面的f(1) )开始,特别是在这样一个例子中,function 调用的后缀表达式命名了两个 function 模板,我们预计其中一个模板会被 SFINAE 拒绝。

#include <type_traits>

// A: expected to be viable
template<typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>
constexpr bool f(T) { return true; }

// B: expected to be rejected
template<typename T, std::enable_if_t<!std::is_integral_v<T>>* = nullptr>
constexpr bool f(T) { return false; }

static_assert(f(1));

The function call above will, as per [over.match.call.general]/1 , result in overload resolution applied as specified in [over.call.func] , particularly [over.call.func]/3 :根据[over.match.call.general]/1 ,上面的 function 调用将导致按照[over.call.func]中指定的方式应用重载决议,特别是[over.call.func]/3

[over.match.call.general]/1 In a function call [over.match.call.general]/1在 function 通话中

postfix-expression ( expression-list_opt )

if the postfix-expression names at least one function or function template, overload resolution is applied as specified in [over.call.func].如果后缀表达式命名至少一个 function 或 function 模板,则按照 [over.call.func] 中指定的方式应用重载决策。

[over.call.func]/3 [...] The name is looked up in the context of the function call following the normal rules for name lookup in expressions ([basic.lookup]). [over.call.func]/3 [...] 在 function 调用的上下文中查找名称,遵循表达式中名称查找的正常规则 ([basic.lookup])。 The function declarations found by that lookup constitute the set of candidate functions.该查找找到的 function 声明构成了候选函数集。 [...] [...]

[over.match.general]/2 decribes the high-level steps of overload resolution: [over.match.general]/2描述了重载解析的高级步骤:

Overload resolution selects the function to call in seven distinct contexts within the language:重载解析选择 function 在语言中的七个不同上下文中调用:

  • (2.1) invocation of a function named in the function call syntax; (2.1) 调用 function 调用语法中命名的 function;
  • [...] [...]

Each of these contexts defines the set of candidate functions and the list of arguments in its own unique way.这些上下文中的每一个都以自己独特的方式定义了候选函数集和 arguments 的列表。 But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases :但是,一旦确定了候选函数和参数列表,最佳 function 的选择在所有情况下都是相同的

  • [...] [...]

Telling us that selecting a function during overload resolution works in three different phases:告诉我们在重载解析期间选择 function 分三个不同的阶段进行:

  1. Identify the candidate functions and associated argument lists确定候选函数和相关参数列表
  2. From these, select the subset of viable functions从这些,select可行功能的子集
  3. From these, select the best viable function based on ranking via implicit conversion sequences matching arguments with parameters从这些中,select 是最佳可行的 function基于通过隐式转换序列匹配 arguments 与参数的排名

SFINAE works in the domain of 1 whilst identifying candidate functions, not so much by rejecting potential candidates but rather by failing to generate a potential candidate when such a potential candidate is a function template (specialization), particularly failing due to template deduction failures. SFINAE 在 1 的领域工作,同时识别候选函数,与其说是拒绝潜在候选函数,不如说是在潜在候选函数是 function 模板(专业化)时未能生成潜在候选函数,尤其是由于模板推导失败而失败。 Our example is in the context of invocation of a function named in the function call syntax, but for the context of finding the normative rules that governs SFINAE, it is representable also for other contexts as listed by [over.match.general]/2.我们的示例是在调用 function 调用语法中命名的 function 的上下文中,但是对于查找管理 SFINAE 的规范规则的上下文,它也可以表示为 [over.match.general]/2 列出的其他上下文.

[over.match.funcs] , particularly [over.match.funcs.general]/7 tells us that we enter the domain of template argument deduction for candidates that are function templates: [over.match.funcs] ,特别是[over.match.funcs.general]/7告诉我们,我们为 function 模板的候选人输入模板参数推导域:

In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction ([temp.over], [temp.deduct]) [...] Those candidates are then handled as candidate functions in the usual way.在候选者是 function 模板的每种情况下,候选者 function 模板专业化是使用模板参数推导生成的([temp.over],[temp.deduct])[...]然后将这些候选者作为通常的候选函数处理方法。

[temp.over] covers overload resolution rules specific to function templates and [temp.over]/1 , in particular, contains one of two essential rules which govern SFINAE: [temp.over]涵盖特定于 function 模板和[temp.over]/1的重载解决规则,特别是,包含管理 SFINAE 的两个基本规则之一:

When a call to the name of a function or function template is written [...] template argument deduction [...] [is] performed [...] if the argument deduction and checking succeeds [...] [the] function template specialization [...] is added to the candidate functions set to be used in overload resolution.当调用 function 或 function 模板的名称时写入 [...] 模板参数推导 [...] [是] 如果参数推导和检查成功 [...] ] function 模板特化 [...] 添加到候选函数集以用于重载解析。 If , for a given function template, argument deduction fails or the synthesized function template specialization would be ill-formed, no such function is added to the set of candidate functions for that template .如果对于给定的 function 模板,参数推导失败或者合成的 function 模板特化形式不正确,则不会将这样的 function 添加到该模板的候选函数集中

Namely that a template argument deduction failure in the phase of finding candidate functions during overload resolution results in the function not being included in the set of candidates.即,在重载决策期间寻找候选函数阶段的模板参数推导失败导致 function 未包含在候选集中。

[temp.deduct] covers template argument deduction, and includes several ways in which it can fail, eg [temp.deduct]/2 , /5 , and /7 , but the second essential rule which governs SFINAE is particularly found in [temp.deduct]/8 : [temp.deduct]涵盖模板参数推导,并包括几种可能失败的方式,例如[temp.deduct]/2/5/7 ,但是管理 SFINAE 的第二个基本规则特别出现在[temp .扣除]/8 :

/7 The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. /7替换发生在 function 类型和模板参数声明中使用的所有类型和表达式中。 [...] The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered. [...] 替换按词汇顺序进行,并在遇到导致演绎失败的条件时停止。 [...] [...]

/8 If a substitution results in an invalid type or expression, type deduction fails . /8如果替换导致无效类型或表达式,则类型推导失败 An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments.如果使用替换的 arguments 编写,则无效类型或表达式是一种格式错误的类型或表达式,需要进行诊断。

[...] [...]

Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.只有 function 类型、其模板参数类型及其显式说明符的直接上下文中的无效类型和表达式才能导致推导失败。

The latter paragraph of [temp.deduct]/8 is the reason for some notoriously tricky non-SFINAE hard failures when using generic lambdas in return type deduction. [temp.deduct]/8 的后一段是在返回类型推导中使用通用 lambda 时出现一些非常棘手的非 SFINAE 硬故障的原因。 See eg P0238R1 for details.详情参见例如P0238R1

If we return to the function call f(1) in the example above, the function template B will be rejected as a candidate as template argument deduction fails when replacing T in std::enable_if_t<:std::is_integral_v<T>>* , namely in the 2nd type template parameter of the template-head, with substitution of the deduced T ( int ) resulting in an invalid type ( std::enable_if_t<:std::is_integral_v<int>>* ).如果我们返回上例中的 function 调用f(1) ,function 模板B将被拒绝作为候选者,因为在std::enable_if_t<:std::is_integral_v<T>>*中替换T时模板参数推导失败,即在模板头的第二个类型模板参数中,替换推导的T ( int ) 导致无效类型 ( std::enable_if_t<:std::is_integral_v<int>>* )。

[temp.deduct]/5 When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template and the function type are replaced with the corresponding deduced or default argument values . [temp.deduct]/5当所有模板arguments已经从默认模板arguments推导出来时,模板的模板参数列表中的模板参数和function类型的所有使用都被替换为相应的推导或默认参数值 If the substitution results in an invalid type, as described above, type deduction fails.如果替换导致无效类型,如上所述,类型推导失败。

Finally, [temp.deduct]/5 is also where we find rules for constraints, which are not a SFINAE mechanism, but a separate step where non-satisfaction results in type deduction failure.最后,[temp.deduct]/5 也是我们找到约束规则的地方,这不是 SFINAE机制,而是一个单独的步骤,不满足会导致类型推导失败。

/5 [...] If the function template has associated constraints ([temp.constr.decl]), those constraints are checked for satisfaction ([temp.constr.constr]). /5 [...] 如果 function 模板具有关联约束 ([temp.constr.decl]),则会检查这些约束是否满足要求 ([temp.constr.constr])。 If the constraints are not satisfied, type deduction fails.如果不满足约束,则类型推导失败。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM