I have the following interface:
struct A {};
struct B
{
using Cb = std::function<void(A& a)>;
using ConstCb = std::function<void(const A& a)>;
virtual void visit(ConstCb cb) const = 0;
virtual void visit(Cb cb) = 0;
};
B::visit()
shall enumerate all members of itself that are of type A
.
Now, since B::visit() might become complex in deriving classes, so duplicating the implementation of visit()
is not nice. Meyers advises const-casting (cf. here).
So an approach would be:
struct B
{
using Cb = std::function<void(A& a)>;
using ConstCb = std::function<void(const A& a)>;
void visit(Cb cb);
void visit(ConstCb cb) const;
protected:
virtual void doVisit(ConstCb) const = 0;
};
void B::f(ConstCb cb) const
{
doVisit(cb);
}
void B::f(Cb cb)
{
static_cast<const B*>(this)->f(/* somehow cast cb to ConstCb */);
}
However, exactly that missing piece seems to be impossible. I can get a function pointer of cb
using std::function::target()
and then try to cast it to the other type. But this is impossible, since target()
doesn't work for lambdas, they can't be converted to function pointers (only if they have an empty capture).
So I can't think of a solution right now. Does somebody have any ideas? Perhaps the whole approach is flawed already :D
One option would be to introduce a "trampoline" function that does the conversion from non-const
to const
:
void B::f(Cb cb)
{
ConstCb trampoline = [&cb] (const A& arg) {
cb(const_cast<A&>(arg));
};
static_cast<const B*>(this)->f(trampoline);
}
In other words, rather than doing a cast on the function, just define a new function that performs a cast on the argument.