2016-05-24 9 views
4

В C++ я могу создать реализацию интерфейса «на лету» (который идеально связывает переменные локальной области). Не знаете, как объяснить это лучше, так что я положил, что я хотел бы код, чтобы выглядеть (примерно):Могу ли я создать анонимный класс «на лету» (реализация интерфейса) в C++

// Given the following: 

class Visitor 
{ 
    virtual void visit(const Data& data) = 0; 
} 

class DataStore 
{ 
    void visitData(Visitor& visitor) { /** Invokes visitor with each item of data. */ } 
} 

// Imagine one would write something like: 

void inSomeFunction(DataStore& store) 
{ 
    std::string myName = "bob"; 

    class MyVisitor: public Visitor 
    { 
    public: 
     MyVisitor(const std::string& p): person(p); 
     virtual void visit(const Data& data) override 
     { 
      std::cout << person << " is visiting " << data << std::endl; 
     } 
    private: 
     std::string person; 
    } 

    MyVisitor myVisitor(myName); 
    store.visitData(myVisitor); 
} 

// Instead of the above, I want to write instead something like: 

void inSomeFunction(DataStore& store) 
{ 
    std::string myName = "bob"; 

    store.visitData(
     class: public MyVisitor 
     { 
      virtual void visit(const Data& data) override 
      { 
       std::cout << myName << " is visiting " << data << std::endl; 
      } 
     } 
    ); 
} 

Я понимаю, что я спрашиваю 2 вопроса здесь - вы можете создать класс Anonymouse, как это, и вы можете привязать переменные из локальной области (например, я упомянул myName). Но мне нужно, чтобы это было полезно.

Если вышеуказанное не может быть сделано, то, что ближе всего, можно использовать для использования C++ 11 lambdas или аналогичного с точки зрения краткости/отсутствия шаблона.

+1

Вы можете достичь того, чего хотите, но не так, как хотите. Я бы использовал функтор для чего-то подобного или посетителя, который принимает указатель на функцию, тогда вы можете просто использовать лямбда. – csl

+0

Моя единственная проблема заключается в том, что способ, которым вы указываете подпись типа для указателя функции в C++, ужасен. Например. Какова была бы подпись метода visitData, если бы я использовал указатель на функцию вместо интерфейса? – nappyfalcon

+0

Если нет нового, лучшего способа указать тип для параметров, которые принимают указатель на функцию ...? – nappyfalcon

ответ

9

Если есть иметь интерфейс, то вы должны делать то, что Kerrek предложил.

Но еще лучше было бы изменить свой интерфейс от:

class DataStore 
{ 
    void visitData(Visitor& visitor) { 
     // bunch of calls to visitor.visit(...) ... 
    } 
} 

к:

template <class Visitor> 
    void visitData(Visitor visitor) { 
     // bunch of calls to visitor(...) ... 
    } 

Это позволит вам просто написать:

std::string myName = "bob"; 
store.visit([myName](const Data& data) { 
    std::cout << myName << " is visiting " << data << std::endl; 
}); 

, который является гораздо более естественно, по-моему. Если интерфейс DataStore находится вне вашего контроля, тогда этот ответ полностью спорный.

+0

Это выглядит фантастически ... но я не понимаю, как это работает. Как «[myName] (const Data & data) { std :: cout << myName <<" посещает "<< данные << std :: endl; }" сопоставляется с параметром Visitor в методе visitData? – nappyfalcon

+0

@nappyfalcon - поскольку механизм C++ _template_ использует утиную типизацию - в этом случае параметр _template type_ 'Visitor' _used_, вызывая' operator() 'на экземплярах этого ... так что тип _any_, который реализует' operator() 'works - и это включает лямбды. Ключом является то, что метод 'visitData' был изменен Барри на« метод шаблона ». – davidbak

6

Вы можете сделать это с еще одним уровнем косвенности:

#include <functional> 
#include <utility> 

class AdHocVisitor : public Visitor 
{ 
public: 
    explicit AdHocVisitor(std::function<void(const Data&)> f) : f_(std::move(f)) {} 
    void visit(const Data& data) override { f_(data); } 
private: 
    std::function<void(const Data&)> f_; 
}; 

Использование:

AdHocVisitor v([a, &b, this](const Data&) {}); 
store.visitData(v);