当前位置:网站首页>Tips for this week 134: make_ Unique and private constructors

Tips for this week 134: make_ Unique and private constructors

2022-07-07 17:57:00 -Flying crane-

As totw#134 Originally published in 2017 year 5 month 10 Japan
By Google engineers Yitzhak Mandelbaum A literary creation

therefore , You read the tips #126 And prepare to leave something new . Everything is normal , Until you try to use absl::make_unique And use private constructors to construct objects , But the compilation failed . Let's take a look at a specific example of this problem , In order to understand what went wrong . then , We can discuss some solutions .

Example : Manufacturing widgets

You are defining a class to display widgets . Each widget has an identifier , These identifiers are subject to certain constraints . To ensure that these constraints are always met , You will be Widget The constructor of the class is declared private , And provide factory functions for users Make, Used to generate widgets with appropriate identifiers .( Suggestions on why factory functions are better than initialization methods , See Tips #42.)

class Widget {
    
 public:
  static std::unique_ptr<Widget> Make() {
    
    return absl::make_unique<Widget>(GenerateId());
  }

 private:
  Widget(int id) : id_(id) {
    }
  static int GenerateId();

  int id_;
}

When you try to compile , You will get the following error :

error: calling a private constructor of class 'Widget'
    {
     return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
                                 ^
note: in instantiation of function template specialization
'absl::make_unique<Widget, int>' requested here
    return absl::make_unique<Widget>(GenerateId());
                ^
note: declared private here
  Widget(int id) : id_(id) {
    }

When make When the private constructor has been accessed ,absl::make_unique Unable to work ! Be careful , This problem also appears under friends . for example :Widget Friends use absl::make_unique To construct Widget There will be the same problem .

Suggest

We recommend any of the following alternatives :

  • Use new and absl::WrapUnique, But please explain your choice . for example :Widget Friends use absl
        //  Use new Access non constructor only 
        return absl::WrapUnique(new Widget(...));
    
  • Consider whether constructors can be safely exposed . If so , Then expose and record it when it is suitable for direct construction .

In many cases , Marking constructors private is overdesign . In these cases , The best solution is to mark your constructors public and record their correct use . However , If your constructor needs to be private ( for instance , To ensure class invariants ), Then use new and WrapUnique.

Why can't I friend absl::make_unique?

You may want to be friends absl::make_unique, So it can access your private constructor . It's a bad idea , There are several reasons :

First , Although a comprehensive discussion of friend practice is beyond the scope of this tip , But a good rule of thumb is “ Don't be a long-distance friend ”. otherwise , You will create a friend competition statement , A statement that is not maintained by the owner . See also style guide recommendations .

secondly , Please note that , You rely on absl::make_unique Implementation details , That is, it directly calls new. If it is refactored to call indirectly new—— for example , In the structure C++14 Or later ,absl::make_unique yes std::make_unique Another name for , And the friend declaration is invalid .

Last , Through friends absl::make_unique, You allow anyone to create objects in this way , So why not declare your constructor public , Then completely avoid this problem ?

std::shared_ptr Well ?

about std::shared_ptr, It's a little different . There is no absl::WrapSahred And simulation (std::shared_ptr(new T(…))) There are two distributions involved , among std::make_shared You can use one to complete . If this difference is important , Then consider idioms : Let the constructor take special tags that only specific code can create . for example :

class Widget {
    
  class Token {
    
   private:
    Token() {
    }
    friend Widget;
  };

 public:
  static std::shared_ptr<Widget> Make() {
    
    return std::make_shared<Widget>(Token{
    }, GenerateId());
  }

  Widget(Token, int id) : id_(id) {
    }

 private:
  static int GenerateId();

  int id_;
};

About idioms , Refer to the following article :
More Useful Empty Classes
Passkey Idiom and Better Friendship in C++

原网站

版权声明
本文为[-Flying crane-]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207071522592288.html