2013-05-03 2 views
0

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

В любом случае, у меня есть класс Tree, который имеет класс Tree::Visitor с одной функцией виртуального обратного вызова, называемой visit(/*args*/). Этот класс Visitor выполняет рекурсивную прогулку по всем узлам. С этим обратным вызовом я могу собирать данные с каждого узла (или лучше я могу извлечь пути дерева (что в основном я делаю с этой функцией).

Так что я беру лямбду и внутри я использую класс для реализовать функцию visit обратного вызова, выводя из Tree::Visitor.

// Tree class, a rough view how it looks 
class Tree { 

    // ... 

    // Visitor class for recursive walking the tree 
    class Visitor { 
     // 
     void count(/* ... */) { 
      // in here the implemented visit(/*args*/) fct is called 
     } 

     // ... 
     void triggerVisit() { 
      // ... 
      count(/* ... */); 
      // ... 
     } 

     // visitor callback 
     virtual void visit(/* args */) = 0; 
    }; 
}; 

class A { 
    Tree tree; 
    PriorityQueue que; 

    A() : tree(), que(maxEntries) {} 

    // first build the tree ... 
    void buildTheTree() { 
     tree.buildTree();    
    } 

    // walk the tree 
    void visitTheTree() { 

     std::shared_ptr<Tree::Visitor>(
      [&]()->Tree::Visitor * { 

      // this class implements visit(/*args*/) 
      class MyVisitor : public Tree::Visitor { 
       A& parent; // pointer to A 

       Myvisitor(A& p) 
       : Tree::Visitor(p.tree), parent(p) {}  

       // implementation 
       virtual void visit(/* args */) { 

        // ... get somedata 

        if (/* condition true */) { 
         parent.que.push(somedata); 
        } 
       } 
      }; 

      return new MyVisitor(*this); 

      }() 
     )->triggerVisit(); 

     // get the collected data from que 
     while(que.size() > 0) { 
      // ... 
     } 
    } 
}; 

в основном это то, что у меня есть, и она работает без проблем.

у меня есть приоритет очереди que, которую я использую для хранения somedata, которые являются n верхние набранные узлы дерево. В настоящее время que определяется как член класса A, который мне не нравится, потому что мне просто нужно собирать данные внутри члена visitTheTree, поэтому он может быть скорее локальной переменной Так что мой вопрос - это скорее вопрос дизайна/стиль, и у меня такое чувство, что я скучаю по стандарту C++ 11 (возможно).

Я попытался определить que внутри visitTheTree() и передать его конструктору MyVisitor. Как-то это работает некорректно, по крайней мере, я не получу надлежащих/полных результатов, которые я ожидаю. Когда я определяю переменную queue Priority как член A (как она есть сейчас) и обращается к ней с родительским указателем в MyVistor, я получаю правильные результаты, и все в порядке.

Есть ли хороший способ определить que локально в VisitTheTree() вместо определения его в классе A в качестве члена? Я знаю, что мне нужно передать его конструктору, поскольку я не могу получить доступ к переменным за пределами MyVistor (точно так же).

BTW, я нашел вопрос C++0x - lambda expression does look same as Java's anonymous inner class?, который близок к проблеме/вопросу, который у меня есть. Интересен ответ от Йоханнеса.

Любые намеки или идеи приветствуются. Спасибо за ваши мысли и помощь!

+1

Есть ли вообще с нуля вариант? У меня создалось впечатление, что много кода, который вы наметили, не обязательно - я бы зашел так далеко, чтобы сказать, что интересные части - это то, что вы оставили. Вы знакомы со сгибом (который обычно принимает вид 'std :: accumulate' в C++)? Это может быть подходящим способом объединения вашего результата при просмотре данных. Что-то еще может быть уместным, но зависит от того, как вы просматриваете данные и какой результат вы хотите. Это недостающие интересные биты. –

+0

@LucDanton Я никогда не использовал std :: accumulate, я думаю, что не могу использовать его в этом случае. Возможно, если бы у меня был контейнер (например, вектор объектов), я мог бы пойти именно так. Я доволен общим дизайном и не думаю о переписывании дерева, потому что он работает хорошо, как есть. Я просто думаю об этой части выше, которая собирает данные с посетителем, может быть, нет другого способа, как сейчас, мне просто интересно, могу ли я как-то переместить переменную 'que' в локальную переменную внутри' visitTheTree', так как мне просто нужно это там. –

+0

Я не рекомендовал использовать 'std :: accumulate'. –

ответ

0

Andreas, я действительно хотел бы помочь вам, но я не вижу способа сделать это, используя ваш дизайн. У меня была похожая ситуация с использованием библиотеки подталкивание график, и я сделал следующее (надеюсь, это поможет вам):

  • Посетитель имеет std::function<RET (/*args*/> элемент, который используется для того, чтобы действие каждый узел вы посещаете. Я бы также сделал эту функцию параметром для конструктора посетителя.
  • Каждый раз, когда вам нужно посетить некоторые узлы, вы будете проходить через новый экземпляр вашего посетителя, передавая новую лямбда-функцию в качестве аргумента.

Я хотел бы попробовать, чтобы обеспечить некоторый пример,

class Tree { 
    ... 
    typedef std::function<void (/*node and args*/)> visit_fn; 

    class Visitor { 
     visit_fn& visitor; 

     public: 
      Visitor(visit_fn f) : visitor(f) {} 
     ... 
    }; 
}; 

class A { 

    ... 

    void visit_tree() { 
     que.clear(); // could also be a local queue object 

     Visitor vis([&](/*args*/) { 
      que.push(some_data); /*I have que because of & in my lambda*/ 
     }); 

     vis.triggerVisit(); 

     // Her I can use my queue member 
    } 

}; 

Теперь, если у вас есть Коммон способ посетить свои элементы, которые вы можете даже передать Functor вашего Visitor, обеспечивая лучшее повторное использование кода.

Я действительно считаю, что лямбда в вашем дизайне не использует привязку [&], и в этом смысле может быть общей функцией, которая, я думаю, была бы более чистой, многоразовой и эффективной.

+1

Julio, спасибо за сообщение. Я рассмотрю ваш путь, может быть, я смогу пойти в вашу сторону. Связывание '[&]' для 'this' внутри лямбда, я также могу использовать' [=] 'здесь, я полагаю. –

+0

Еще один, я только что отредактировал свой код, как следует упомянуть, что существует функция 'count()', которая выполняет некоторый подсчет пути/узла, реализованный 'посещения()' fct в классе «Дерево :: Посетитель» получает вызов в 'count()'. В закрытом классе лямбда я выполняю его. –

+0

Да, вы можете использовать '[=]', но я думаю, что лучший способ ответить на ваш вопрос - удалить переменную-член очереди класса 'A' ​​и использовать локальную очередь непосредственно перед вызовом посетителя (где я ' m вызов 'que.clear()'). Если вы используете локальную очередь, вы можете передать ее просто, как в '[& local_queue]'. –

Смежные вопросы