Skip to content

Instantly share code, notes, and snippets.

@elbeno
Last active June 10, 2018 23:33
Show Gist options
  • Save elbeno/1d2a8778a6c4a954496f to your computer and use it in GitHub Desktop.
Save elbeno/1d2a8778a6c4a954496f to your computer and use it in GitHub Desktop.
Keyed functions in C++
#include <memory>
#include <type_traits>
// Foo is a class that has a private constructor, and it's created by a factory
// function that returns a smart pointer.
class Foo
{
public:
static std::unique_ptr<Foo> Create();
private:
Foo() {}
};
std::unique_ptr<Foo> Foo::Create()
{
// Oh dear, make_unique doesn't work: can't call a private constructor
//return std::make_unique<Foo>();
// We have to do this, which is exception-unsafe and annoying. And if this
// were making a shared_ptr, it would have to do two allocations.
return std::unique_ptr<Foo>(new Foo());
}
// Bar is the same thing as Foo, but now we get around the problem with a "keyed
// function".
class Bar
{
private:
struct key_t {
// give the key an explicit constructor to prevent implicit
// construction from {}
explicit key_t() {}
};
public:
static std::unique_ptr<Bar> Create();
// This constructor works only when you really want it to: no implicit
// conversions interfere with it.
explicit Bar(key_t) {}
// Everything that isn't a key_t is a better match here and is deleted.
template <typename T>
explicit Bar(T) = delete;
};
std::unique_ptr<Bar> Bar::Create()
{
// This works: constructor is public but keyed with a private struct!
return std::make_unique<Bar>(key_t{});
}
struct skeleton_key
{
template <typename T>
operator T&() { return *reinterpret_cast<T*>(this); }
};
int main()
{
//auto foo = Foo::Create(); // error or inefficiency
auto bar = Bar::Create();
//auto bar2 = Bar{{}}; // no sneaky trying to create a key_t without naming it
//auto bar3 = Bar{skeleton_key{}}; // error: constructor deleted
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment