2

Here is a class, Unique, for creating unique objects. Objects are created through the factory method Unique::Get and are uniqued by the value passed to this method. The method checks if there is an object with this value (a key) in the set of unique objects Unique::cache, and if not, creates a new object and inserts in into the set.

I want this class to be of very limited use to users. Specifically, users should not be able to copy objects of this class or create objects not by means of calling the Get method. To achieve this I declare the constructors and the assignment operator as private. But when I do this, I can not use std::set::emplace within the class itself, because the constructor is private.

Possible solution would be declaring the std::set class as a friend, but that would allow users to also use this container with my class. This can be harmful.

Is there a better way?

#include <set>
#include <iostream>
#include <iomanip>

class Unique {
public:
  // user accessible methods
  static const Unique &Get(int key);
  bool operator==(const Unique &other) const {
    return this == &other;
  }
private:
  // user inaccessible methods
  Unique(int key) : key_(key) {}
  Unique(const Unique &) = delete;
  Unique &operator=(const Unique &) = delete;
  int key_;
private:
  // cache related stuff
  struct Identity {
    Identity(int key) : key_(key) {}
    int key_;
  };
  struct Compare {
    struct is_transparent {};
    bool operator()(const Identity &lhs, const Unique &rhs) const {
      return lhs.key_ < rhs.key_;
    }
    bool operator()(const Unique &lhs, const Identity &rhs) const {
      return lhs.key_ < rhs.key_;
    }
    bool operator()(const Unique &lhs, const Unique &rhs) const {
      return lhs.key_ < rhs.key_;
    }
  };
  static std::set<Unique, Compare> cache;
};

std::set<Unique, Unique::Compare> Unique::cache;

const Unique &Unique::Get(int key) {
  Identity identity(key);
  auto it = cache.find(identity);
  if (it == cache.end()) {
    it = cache.emplace(key).first;
  }
  return *it;
}

int main() {
  const Unique &c1 = Unique::Get(1);
  const Unique &c2 = Unique::Get(1);
  std::cout << std::boolalpha << (c1 == c2) << std::endl;
}
  • If you can't copy them, then how were you planning on getting one from the location returned by `Unique::Get`, into the set? Can they be moved? – user253751 Dec 15 '16 at 02:00
  • The emplace method constructs an element in place, it does not require a copy constructor. But it requires a normal constructor to be accessible, and this is the problem. I believe there is some common pattern solving it. – Sergey Barannikov Dec 15 '16 at 02:07
  • 1
    Obviously, if you don't allow users of your class to construct instances, you don't allow them to construct instances inside sets, because then they'd be able to do the thing you're trying to stop them doing. – user253751 Dec 15 '16 at 02:14
  • So it seems like you want your users to be able to construct instances *sometimes* (specifically when those instances are inside a set). Are there any other times you want users to be able to construct instances? – user253751 Dec 15 '16 at 02:31
  • My English is far from perfect, sorry. But I think I have managed to explain my intentions very clearly. I do not want users of my class to be able to use constructors in any direct or indirect way, except through the calling of the factory method. And this method should be able to add an element to the set (which is a member of the class). – Sergey Barannikov Dec 15 '16 at 02:42
  • Oh, oops, my bad. I thought you wanted *users* to be able to make sets, but I was wrong. – user253751 Dec 15 '16 at 03:18

1 Answers1

2

There are several common techniques for preventing direct construction of class instances, except through the "official channels".

Here's the most simple one of them: an example of creating a class that must be constructed using its factory method, yet it has a public constructor:

Header file:

class Object {

    class private_inner {};

public:

    Object(const private_inner &);

    static Object factory();
};

Your implementation class;

static Object::private_inner inner;

Object Object::factory()
{
    return Object(inner);
}

The constructor requires a reference to a private class member. Even though the constructor is public, it is unusable, because the private class member is inaccessible.

Meanwhile, the factory() has no issues constructing itself.

I'm sure that you can adapt this example to your class hierarchy. Note that your factory can call emplace() in C++ library containers without any issues, since your factory will have full access to the private class, and can pass it to the containers' methods.

Sam Varshavchik
  • 84,126
  • 5
  • 57
  • 106
  • Thanks for this, I can use the Identity class as an anchor in my case. Could you also share the other techniques (links are accepted :). – Sergey Barannikov Dec 15 '16 at 02:45
  • [pimpl](http://stackoverflow.com/questions/60570/why-should-the-pimpl-idiom-be-used) would be one. A variation on pimpl is when only a virtual base class gets exposed, with the actual implementation in a subclass which is privately declared. The factory constructs the subclass with `new`, and returns the pointer to the base class. – Sam Varshavchik Dec 15 '16 at 02:55