Last active
June 10, 2018 23:33
-
-
Save elbeno/1d2a8778a6c4a954496f to your computer and use it in GitHub Desktop.
Keyed functions in C++
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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