This question already has an answer here:
- How to avoid C++ anonymous objects 6 answers
For many RAII "guard" classes, being instantiated as anonymous variables does not make sense at all:
{
std::lock_guard<std::mutex>{some_mutex};
// Does not protect the scope!
// The unnamed instance is immediately destroyed.
}
{
scope_guard{[]{ cleanup(); }};
// `cleanup()` is executed immediately!
// The unnamed instance is immediately destroyed.
}
From this article:
Anonymous variables in C++ have “expression scope”, meaning they are destroyed at the end of the expression in which they are created.
Is there any way to prevent the user from instantiating them without a name? ("Prevent" may be too strong - "making it very difficult" is also acceptable).
I can think of two possible workarounds, but they introduce syntactical overhead in the use of the class:
Hide the class in a
detail
namespace and provide a macro.namespace detail { class my_guard { /* ... */ }; }; #define SOME_LIB_MY_GUARD(...) \ detail::my_guard MY_GUARD_UNIQUE_NAME(__LINE__) {__VA_ARGS__}
This works, but is hackish.
Only allow the user to use the guard through an higher-order function.
template <typename TArgTuple, typename TF> decltype(auto) with_guard(TArgTuple&& guardCtorArgs, TF&& f) { make_from_tuple<detail::my_guard>(std::forward<TArgTuple>(guardCtorArgs)); f(); }
Usage:
with_guard(std::forward_as_tuple(some_mutex), [&] { // ... });
This workaround does not work when the initialization of the guard class has "fluent" syntax:
{ auto _ = guard_creator() .some_setting(1) .some_setting(2) .create(); }
Is there any better alternative? I have access to C++17 features.
The only sensible way I think about is to make the user pass the result of guard_creator::create
to some guard_activator
which takes a lvalue-reference as a parameter.
this way, the user of the class has no way but either create the object with a name (the sane option that most developers will do), or new
it then dereference (insane options)
for example, you said in the comments you work on a non allocating asynchronous chain creator. I can think on an API which looks like this:
auto token = monad_creator().then([]{...}).then([]{...}).then([]{...}).create();
launch_async_monad(token); //gets token as Token&, the user has no way BUT create this object with a name
If have access to the full potential of C++17, you can expand the idea of using a static factory function into something usefull: guarantied copy elision makes the static factory function possible even for non-movable classes, and the [[nodiscard]] attributes prompts the compiler to issue a warning if the return value is ignored.
class [[nodiscard]] Guard {
public:
Guard(Guard& other) = delete;
~Guard() { /* do sth. with _ptr */ }
static Guard create(void* ptr) { return Guard(ptr); }
private:
Guard(void* ptr) : _ptr(ptr) {}
void* _ptr;
};
int main(int, char**) {
Guard::create(nullptr);
//auto g = Guard::create(nullptr);
}
You could use an extensible lint tool such as Vera++ https://bitbucket.org/verateam/vera/wiki/Home it lets you lint your code, you can create new rules using Python or tcl (I prefer Python)
A possible flow would be - after each commit, your CI system (e.g Jenkins) will run a job that executes Vera++ and validate such oversights, upon a failure a mail would be issued to the committer.
The canonical way to prevent a class from being instantiated is by making its constructor private
. To actually get one of the desired instances, you call a static
method, which returns a reference to a constructed object.
class Me {
public:
static Me &MakeMe() { return Me(); }
private:
Me();
}; // Me
This doesn't help of course - but it'd probably make the programmer pause!
int main() {
Me(); // Invalid
Me m; // Invalid
Me::MakeMe(); // Valid - but who'd write that?
Me m = Me::MakeMe();
} // main()
I know this isn't a direct analog to the Guard
instances that you describe - but maybe you could adapt the concept?
来源:https://stackoverflow.com/questions/40884335/preventing-users-from-creating-unnamed-instances-of-a-class