【问题标题】:Can SFINAE detect private access violations?SFINAE 可以检测到私有访问违规吗?
【发布时间】:2012-01-24 08:46:01
【问题描述】:

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

【问题讨论】:

  • ...你真的测试过吗?
  • 不,我没有测试它。我不知道一个完全符合的实现。
  • @Mehrdad:但是如何解释测试的输出?你会知道编译器是如何解释它的,但是你怎么知道编译器是否符合标准呢?
  • @meh 我确实做到了。我将它标记为 c++ 并且没有询问任何编译器。如果不是 c++,我的问题会是什么?
  • 不,我对实用且可移植的代码感兴趣。

标签: c++ language-lawyer sfinae


【解决方案1】:

是的。

编辑:§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 的情况下,有一些关于访问控制的特殊情况。

【讨论】:

    【解决方案2】:

    没有。我在路上,没有标准可以引用,但是 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 使之成为可能。

    【讨论】:

    【解决方案3】:

    我不这么认为。

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

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

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

    【讨论】:

    • 嗯,我认为如果模板构造不正确,就会发生 sfinae?
    • 其实在[temp.deduct]中好像有专门的规定可以替代。请参阅我的回答中的 §14.8.2/8。
    【解决方案4】:

    这是一个实现 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-04
      • 1970-01-01
      • 2010-10-03
      • 2011-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多