15

我想知道如果我测试某个班级的某个成员并且该成员是私人的,那么 sfinae 会做出什么反应?它会很难出错还是会说好的还是会以sfinae方式出错?

4

4 回答 4

11

是的。

编辑:来自§14.8.2 [temp.deduct]的 C++11 标准引用

8/如果替换导致无效的类型或表达式,则类型推导失败。无效类型或表达式是如果使用替换参数编写的格式错误的类型或表达式。[ 注意:访问检查是替换过程的一部分。——尾注]

这向我表明private可能会触发 SFINAE 错误。继续阅读:

只有在函数类型及其模板参数类型的直接上下文中的无效类型和表达式会导致推导失败。[注意:替换类型和表达式的评估可能会导致副作用,例如类模板特化和/或函数模板特化的实例化,隐式定义函数的生成等。此类副作用不在“立即上下文”,并可能导致程序格式错误。—结束注释]

“直接上下文”对我来说不是很清楚......但这并不与我的观点相矛盾:)

编辑结束

所以在我看来,它会以 SFINAE 方式出错,Clang 的这段摘录进一步证实了这一点:

// clang/Basic/DiagnosticIDs.h:185-209

  /// \brief Enumeration describing how the the emission of a diagnostic should
  /// be treated when it occurs during C++ template argument deduction.
  enum SFINAEResponse {
    /// \brief The diagnostic should not be reported, but it should cause
    /// template argument deduction to fail.
    ///
    /// The vast majority of errors that occur during template argument
    /// deduction fall into this category.
    SFINAE_SubstitutionFailure,

    /// \brief The diagnostic should be suppressed entirely.
    ///
    /// Warnings generally fall into this category.
    SFINAE_Suppress,

    /// \brief The diagnostic should be reported.
    ///
    /// The diagnostic should be reported. Various fatal errors (e.g.,
    /// template instantiation depth exceeded) fall into this category.
    SFINAE_Report,

    /// \brief The diagnostic is an access-control diagnostic, which will be
    /// substitution failures in some contexts and reported in others.
    SFINAE_AccessControl
  };

在 SFINAE 的情况下,有一些关于访问控制的特殊情况。

于 2012-01-24T09:21:01.917 回答
5

不。我在路上,没有标准可以引用,但 sfinae 发生在编译阶段,编译器检查名称是否存在,并在稍后阶段进行访问控制。

这类似于重载决议,其中考虑所有名称,并且私有匹配更好,但不会编译,尽管还有另一个匹配“ok”但不是私有的。

添加:

核心问题 1170 说:

1170 模板参数扣除期间的访问检查
部分:14.8.2 [temp.deduct]
状态:FDIS 提交者:Adamczyk 日期:2010-08-03

[在 2011 年 3 月的会议上投票加入 WP。]

根据 14.8.2 [temp.deduct] 第 8 段,

访问检查不作为替换过程的一部分进行。因此,当推导成功时,在实例化函数时仍然可能导致访问错误。

这模仿了在重载决议中进行访问检查的方式。然而,经验表明,从演绎失败中排除访问错误会使标准库变得非常复杂,因此应该更改此规则。

提议的决议(2011 年 1 月):

将 14.8.2 [temp.deduct] 第 8 段更改如下:

如果替换导致无效的类型或表达式,则类型推导失败。无效类型或表达式是如果使用替换参数编写的格式错误的类型或表达式。[注意:访问检查不作为替换过程的一部分进行。——尾注]因此,当推导成功时,当函数被实例化时仍然可能导致访问错误。只有无效的类型...

所以我的解释是,这在 C++03 中是不可能的,但 C++11 使它成为可能。

于 2012-01-24T09:11:20.423 回答
2

我不这么认为。

11/4《会员访问控制》(C++03):

对给定结构的解释是在不考虑访问控制的情况下建立的。如果建立的解释使用了不可访问的成员名称或基类,则该构造是错误的。

因此,首先发生重载解决方案,然后应用访问控制。

于 2012-01-24T09:10:50.397 回答
1

这是一个实现 is_comparable 并处理潜在私有运算符 == 的示例。g++-4.7 对此感到窒息,但 g++-4.8 和 clang++ 3.4 在 C++11 模式下正确处理它。

#include <iostream>
#include <utility>
// is_comparable trait
template<class T>
class is_comparable {
  template<typename U> static char (&check (int))[1 + sizeof (decltype (
    std::declval<U>() == std::declval<U>() // trait check
  ))];
  template<typename>   static char (&check (...))[1];
public:
  static constexpr const bool value = sizeof (check<T> (0)) != 1;
};
// tests
class Diff1 {};          // non-comparable
class Diff2 {            // non-comprable, since member is private
  bool operator== (const Diff2&);
};
struct EqM { bool operator== (EqM); };  // comparable
struct EqG {};                          // comparable
bool operator== (const EqG&, const EqG&);
int
main ()
{
  std::cout << "is_comparable:";
  std::cout << " void=" << is_comparable<void>::value;
  std::cout << " Diff1=" << is_comparable<Diff1>::value;
  std::cout << " Diff2=" << is_comparable<Diff2>::value;
  std::cout << " int=" << is_comparable<int>::value;
  std::cout << " EqM=" << is_comparable<EqM>::value;
  std::cout << " EqG=" << is_comparable<EqG>::value;
  std::cout << "\n";
  return 0;
}
// $ clang++ is_comparable.cc -std=c++11 && ./a.out
// is_comparable: void=0 Diff1=0 Diff2=0 int=1 EqM=1 EqG=1
于 2014-07-24T19:06:04.813 回答