2016-02-10 6 views
1

У меня есть следующий интерфейс:Реализовать сопзЬ и неконстантную версии методов, берущих обратного вызова

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() Перечислим все члены, которые себе типа A.

Теперь, так как B :: visit() может стать сложным при выводе классов, поэтому дублирование реализации visit() не является приятным. Мейерс советует консистенцию (см. here).

Так подход был бы:

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 */); 
} 

Однако именно это недостающая часть, кажется, невозможно. Я могу получить указатель на функцию cb с использованием std::function::target(), а затем попытаться применить его к другому типу. Но это невозможно, так как target() не работает для лямбда, они не могут быть преобразованы в указатели функций (только если у них есть пустой захват).

Поэтому я не могу придумать решение прямо сейчас. Есть ли у кого-нибудь идеи? Может быть, весь подход испорчен уже: D

ответ

4

Одним из вариантов было бы ввести функцию «трамплин», что делает переход от не- const к const:

void B::f(Cb cb) 
{ 
    ConstCb trampoline = [&cb] (const A& arg) { 
     cb(const_cast<A&>(arg)); 
    }; 
    static_cast<const B*>(this)->f(trampoline); 
} 

Другими словами, вместо того, чтобы делать на основе функции , просто определите новую функцию, выполняющую листинг на аргументе .