【问题标题】:Can't use private constructor of befriended CRTP class不能使用友好的 CRTP 类的私有构造函数
【发布时间】:2019-03-27 18:09:07
【问题描述】:

我有使用这种设计的代码,经过简化以获得这个 MCVE - 代码和编译器错误随之而来。

基本问题是我认为与 CRTP 类交朋友将允许模板化基类访问派生 CRTP 类的私有成员,包括其私有构造函数。

但显然不是。为什么?

this code on wandbox:

#include <iostream>
using namespace std;

template <class CRTP>
class Base
{
  friend CRTP;
public:
  static void Factory()
  {
      cout << "Base::Factory()" << endl;
      CRTP x;
      x.Hello();
  }
  virtual void Hello() { cout << "Base::Hello()" << endl; }
protected:
  Base() { cout << "Base::Base()" << endl; }
  virtual ~Base() { cout << "Base::~Base()" << endl; }
};


class Derived final : public Base<Derived>
{
public:
  void Hello() override;
private:
  Derived() { cout << "Derived::Derived()" << endl; }
  ~Derived() override { cout << "Derived::~Derived()" << endl; }
};

int main()
{
    Derived::Factory();
    // Expected output:
    //   Base::Factory()
    //   Base::Base()
    //   Derived::Derived()
    //   Derived::Hello()
    //   Derived::~Derived()
    //   Base::~Base()
}

并得到这个编译器错误(来自 clang 9.0.0,但 gcc 以同样的方式抱怨):

prog.cc:12:12: error: calling a private constructor of class 'Derived'
      CRTP x;
           ^
prog.cc:33:14: note: in instantiation of member function 'Base<Derived>::Factory' requested here
    Derived::Factory();
             ^
prog.cc:27:3: note: declared private here
  Derived() { cout << "Derived::Derived()" << endl; }
  ^
prog.cc:12:12: error: variable of type 'Derived' has private destructor
      CRTP x;
           ^
prog.cc:28:11: note: declared private here
  virtual ~Derived() { cout << "Derived::~Derived()" << endl; }
          ^
2 errors generated.

(仅供参考:用例是我希望模板基类通过静态工厂控制(CRTP)派生类实例的生命周期——包括构造。所以我希望派生类将其构造函数声明为私有,但可访问父类的静态工厂方法。此示例显示了在堆栈上创建的派生类实例,但如果在堆中创建(并返回)也会发生相同的错误。)

【问题讨论】:

    标签: c++ friend crtp


    【解决方案1】:

    你在错误的类中得到了friend 声明。 Derived 需要将Base&lt;Derived&gt; 声明为好友,因为好友可以访问私有成员。 (一个类将另一个类声明为友元。一个类不将自己声明为另一个类的友元。)

    你想添加

    friend Base<Derived>;
    

    进入Derived 类声明。

    【讨论】:

    • 是的,倒退了。谢谢!顺便说一句,friend Base 显然也有效。
    【解决方案2】:

    您提到的具体问题是您将friend 声明放在Base 而不是Derived

    下一个问题是Derived::Hello() 没有实现。如果您不需要(如上面的示例)仅在Base 中实现它并删除virtual 声明。 Derived 无论如何都会继承它。

    以下作品:

    #include <iostream>
    
    using namespace std;
    
    template <class CRTP>
    class Base
    {
     public:
      static void Factory()
      {
        cout << "Base::Factory()" << endl;
        CRTP x;
        x.Hello();
      }
      void Hello() { cout << "Base::Hello()" << endl; }
    
     protected:
      Base() { cout << "Base::Base()" << endl; }
      virtual ~Base() { cout << "Base::~Base()" << endl; }
    };
    
    class Derived final : public Base<Derived>
    {
      friend Base<Derived>;
    /*^^^^^^ friend declaration goes here, not in Base */
    
      Derived() { cout << "Derived::Derived()" << endl; }
      ~Derived() override { cout << "Derived::~Derived()" << endl; }
    }; 
    

    输出:

    Base::Factory()
    Base::Base()
    Derived::Derived()
    Base::Hello()
    Derived::~Derived()
    Base::~Base()
    

    【讨论】:

    • 是的,倒退了。谢谢!顺便说一句,朋友 Base 显然也有效。而且您对错误有敏锐的洞察力(也包括对上述问题的评论中的错误)!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-24
    • 2022-07-22
    • 2023-04-07
    • 2014-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多