【问题标题】:Can friend class be declared conditionally in C++03?可以在 C++03 中有条件地声明朋友类吗?
【发布时间】:2012-12-21 06:26:07
【问题描述】:

我只想在某些(编译时)条件为真时声明一个友元类。例如:

// pseudo-C++
class Foo {
    if(some_compile_time_condition) {
        friend class Bar;
    }
};

我在互联网上没有找到任何解决方案。我浏览了问题Generating Structures dynamically at compile time 的所有答案。他们中的许多人使用 C++11 std::conditional,但我想知道是否可以在 C++03 中做到这一点不使用预处理器

这个解决方案https://stackoverflow.com/a/11376710/252576 将不起作用,因为friendship 没有被继承(friend class with inheritance)。

编辑只是为了让这个更容易看到,如下评论中所述:这个要求是不寻常的。这是我正在从事的硬件仿真新研究项目的一部分。测试台是用 C++ 编写的,我想以波形显示变量。我研究了各种其他选项,并发现出于实际考虑我需要使用friend class。朋友将捕获值并生成波形,但我希望仅在需要波形时才让朋友,而不是一直。

【问题讨论】:

  • 我认为你需要一个预处理器
  • 您能更具体地说明您要做什么吗?您可以尝试我们/我在 SO 上设计的所谓“passkey idiom”(即不广为人知)。
  • 这是实际开发中非常不寻常的要求(另一方面,如果出于理论目的这样做是绝对可以的)。
  • @GManNickG 我在一个新的硬件仿真研究项目中使用它。测试台代码是用 C++ 编写的,我想提供一个新功能来在模拟期间查看波形中的成员变量值。 friend 类将捕获值并生成波形。我希望那个朋友只在需要波形时出现,而不是总是出现。
  • @Yury 你说得对,这很不寻常。我已经在上面评论了用例。

标签: c++ friend c++03


【解决方案1】:

使用friend std::conditional<C, friendclass, void>::type;,其中C 是您的条件。非类类型的朋友将被忽略。

条件模板在 C++03 中很容易实现。但是由于 C++03 不支持 typedef 的朋友,你需要在那里使用以下语法

namespace detail { class friendclass {}; }

class Foo {
  friend class std::conditional<C, 
    friendclass, detail::friendclass>::type::friendclass;
};

请注意,详细虚拟类名称需要与此解决方法中潜在朋友的名称匹配。

【讨论】:

  • 我想关于虚拟类名的注释与类型之后的::friendclass 部分有关 => 你能解释一下这是什么(我猜是注入类名)以及为什么它是必要的?天真地我会停在friend class std::conditional&lt;...&gt;::type
  • @mat ::type 之后的部分是必需的,因为 ::type 是 typedef。但是,除非 typedef 在与相应别名类相同的范围内声明,否则不允许在 typedef 名称前加上 struct、class 或 union(或 enum)。因此我们访问注入的类名,它不是 typedef 名称。
  • @JohannesSchaub-litb:那么,如果 Foo 类型的对象包含在不同的 TU 中,并且具有不同的 C 值,是否违反了 ODR?我想是的(基于 3.2/5 - 查找时名称应指同一实体)。虽然在实践中它不应该引起问题。
  • @richard 是的,我同意。条件在所有 TU 中必须具有相同的值。
【解决方案2】:

[class.friend]/3 告诉我们:

不声明函数的友元声明应具有以下形式之一:
友详细说明类型说明符;<br>友人简单类型说明符;
@ 987654323@friend typename-specifier ;

因此,如果没有宏,就不可能有条件地声明一个类的朋友。

【讨论】:

    【解决方案3】:

    似乎,不幸的是,这在 C++ 编译器中是不可能的:也就是说,似乎只有预处理器可以提供帮助。 注意:Johannes 有提议,所以有希望!

    但我会注意到:

    • 友谊不需要你实际使用它
    • friendship 是一种纯编译时构造(如访问说明符),不会对任何主要编译器产生任何运行时损失

    没有理由不拥有无条件的友谊,但只有在满足某些条件(静态或动态)时才使用它。

    注意:在未来,这是 static_if 提案可以涵盖的内容。

    【讨论】:

      【解决方案4】:

      注意: Johannes 已经非常成功了。在 '03 你不能成为朋友 一个 typedef - 但如果你知道你有一个类,那么你可以参考 这是injected class name

      Johannes 的回答还具有使用标准库功能的好处,这也总是一件好事。

      #define some_compile_time_condition 0
      
      class Foo;
      
      template <int Condition> class  TestCondition {
      private:
        friend class Foo;
        struct Type {
          struct Bar;
        };
      };
      
      template <> class TestCondition<1> {
      public:
        typedef Bar Type;
      };
      
      struct Bar
      {
      public:
        void foo (Foo &);
      };
      
      class Foo {
      private:
        friend struct TestCondition< some_compile_time_condition >::Type::Bar;
        int m_i;
      };
      
      void Bar::foo (Foo & foo)
      {
        foo.m_i = 0;
      }
      

      它与Foo 总是有一个 朋友,但结识的阶级会根据 选项。

      一个有趣的附带问题是是否违反了 ODR Foo 的版本有和没有some_compile_time_condition 设置为 1。

      【讨论】:

        【解决方案5】:

        我认为您需要 1 个预处理器并在其中编写源代码。

        bool flag = false;
        #ifdef _MY_FRIEND_
            friend class sample
            flag = true;
        #endif
        
        if (flag)
        {
         ...
         ...
         ...
        }
        

        class Foo {
        #ifdef _MY_FRIEND_
            friend class Bar;
        #endif
        }
        

        };

        这里 _MY_FRIEND_ 是一个预处理器,如果您添加该预处理器,那么在编译时您的类 Bar 将成为朋友类...当您需要类 Bar 作为朋友类时,您可以在任何地方使用该预处理器。否则在没有预处理器的情况下编译,那么它不允许你将 Bar 添加为 Foo 的朋友类

        如果我理解错了问题,请纠正我。

        【讨论】:

          猜你喜欢
          • 2023-03-30
          • 1970-01-01
          • 2014-07-23
          • 2017-02-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多