Let's say I have a class FunctionWrapper
defined like this:
struct FunctionWrapper
{
FunctionWrapper(std::function<void()> f);
// ... plus other members irrelevant to the question
};
I'd like to prevent implicit conversions from std::function<void()>
to FunctionWrapper
, but allow constructing a FunctionWrapper
using a brace initialisation syntax (that is, using list initialisation with a single argument). In other words, I'd like this:
void foo();
void wrap(FunctionWrapper);
wrap(foo); // (1) error
wrap({foo}); // (2) OK
wrap(FunctionWrapper{foo}); // (3) OK
Is there a way to achieve that? The way I've defined the class above is not it: this allows implicit conversions, so (1) compiles.
If I add explicit
to the constructor:
struct FunctionWrapper
{
explicit FunctionWrapper(std::function<void()> f);
// ... plus other members irrelevant to the question
};
it doesn't help either, as that goes "too far" and disallows (2) as well as (1).
Is there a way to achieve "middle ground" and have (2) compile while (1) produces an error?
Is there a way to achieve that?
Yes. You already have it.
wrap(foo);
For this to work, it would involve two user-defined conversions: void(*)() --> std::function<void()> --> FunctionWrapper
, but we're only allowed up to one user-defined conversion. So this is an error and will be unless you add a separate constructor to FunctionWrapper
to allow for it.
wrap({foo});
This is already fine, we're copy-list-initializing FunctionWrapper
so the above limitation doesn't apply.
wrap(FunctionWrapper{foo});
This is clearly fine.
Note that this also provides a path forward for those cases where your first example actually worked. Let's say you had:
struct Wrapper {
Wrapper(int ) { }
};
foo(0); // want this to fail
foo({0}); // want this to be OK
foo(Wrapper{0}); // ... and this
You can't make the constructor explicit
, since that causes foo({0})
to fail as well. But you can simply add another layer of indirection with another wrapper:
struct AnotherWrapper {
AnotherWrapper(int i): i{i} { }
int i;
};
struct Wrapper {
Wrapper(AnotherWrapper ) { }
};
Here, wrap(0)
fails, but wrap({0})
and wrap(Wrapper{0})
are both OK.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With