4

My class has the following structure:

class S {
  public:
    S() {}
};

class T {
  private:
    std::unique_ptr<S> a;
    T(S);

  public:
    static std::unique_ptr<T> make_item() {
        std::unique_ptr<S> s_instance = std::make_unique<S>();
        return std::make_unique<T>(std::move(s_instance));
    }
};

However, when I try to make a unique_ptr in the make_item, it sees the constructor as private.

Is there a way to allow the use of the private constructor in a static member function of the class itself? Because one member is a unique_ptr to S (a rather heavy object), we wish not to use a copy.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Ward Segers
  • 519
  • 5
  • 17
  • 1
    I second the duplicate proposition, but `std::make_unique` can be replaced with `std::unique_ptr(new X);` without any performance loss, so the tricks from that question are not required. – Yksisarvinen Dec 14 '20 at 09:50
  • @Yksisarvinen But that's not exception safe! A factory pattern is the better approach, if this constraint is relevant. – Secundi Dec 14 '20 at 12:34
  • @Yksisarvinen By "without any performance loss" you mean not considering the extra allocation? – pooya13 Jul 03 '21 at 20:31

2 Answers2

11

As proposed by yksisarvinen in the comments, a way to solve this is to just replace make_unique<T> by std::unique_ptr<T>(new T(S)).

class S {
  public:
    S() {}
};
class T {
  private:
    std::unique_ptr<S> a;
    T(S);

  public:
    static std::unique_ptr<T> make_item() {
        // Create S
        std::unique_ptr<S> s_instance = std::make_unique<S>();
        return std::unique_ptr<T>(new T(s_instance));
    }
};
Ward Segers
  • 519
  • 5
  • 17
  • answering your own question is fine, but note that the question is a bit unclear. I suggest you to add the missing `;`, the declaration of `S` and the error message, so others can actually see the problem – 463035818_is_not_an_ai Dec 14 '20 at 10:49
  • 1
    So what is the benefit of using std::make_unique here? We use if for its safety which is not provided by 'new'. I think a better answer could be found here: https://stackoverflow.com/questions/50995599/make-unique-doesnt-compile-for-creating-a-singleton-instance – AshkanVZ Nov 19 '21 at 11:44
  • @AshkanVZ is correct, the other answer he referred to is better. The accepted answer here is not exception-safe and could potentially leak. – Dalzhim Mar 03 '23 at 17:48
3

It is possible to either declare make_unique and its internal helpers (if any) a friend function, which make the code non-portable (as said here: How to make std::make_unique a friend of my class)

Or make the constructor public but add a private element to its arguments, for example:

class S {
  public:
    S() {}
};

class T {
  private:
    struct Private
    { 
      friend T;
      private:
        explicit Private() = default; 
    };

    std::unique_ptr<S> a;

  public:
    T(S s, Private);

    static std::unique_ptr<T> make_item() {
        auto s_instance = std::make_unique<S>();
        return std::make_unique<T>(std::move(s_instance), Private());
    }
};
AshkanVZ
  • 681
  • 6
  • 14
  • 1
    You can use the pass key idiom to avoid letting just anyone make_unique on your type: https://stackoverflow.com/a/29897660/874660 – Ben Nov 25 '21 at 22:47
  • Thanks for introducing the term "Passkey Idiom", I didn't know the term. Do I understand correctly? Is it the generic idea of the answer I provided? – AshkanVZ Nov 26 '21 at 16:54
  • No, the problem with making make_unique a friend is that then anyone can make_unique so it’s as good as a public c’tor more or less. With the pass key idiom, you have a public c’tor but one of the arguments is of a type that only your class can create so that c’tor is as good as private as far as your API but you can allow others (like make_unique) to call it by passing make_unique an instance of the passkey class. – Ben Nov 27 '21 at 01:57