1

I'm implementing a tree where each Node is only either an InnerNode or a Leaf which I would normally implement like this:

class Node {
public:
    virtual ~Node() = default;
};

class InnerNode: public Node {
    std::vector<std::unique_ptr<Node>> children;
    ...
};

class Leaf: public Node {
    ...
};

Now I would like to have a template method for_each but template methods cannot be virtual. To work around this I could implement for_each like this:

class Node {
    ...
    virtual InnerNode* get_inner_node() {
        return nullptr;
    }
    virtual Leaf* get_leaf() {
        return nullptr;
    }
    template <class F> void for_each(F&& f) {
        if (InnerNode* inner_node = get_inner_node()) {
            inner_node->for_each_inner_node(std::forward(f));
        }
        else if (Leaf* leaf = get_leaf()) {
            leaf->for_each_leaf(std::forward(f));
        }
    }
};

class InnerNode: public Node {
    ...
    InnerNode* get_inner_node() override {
        return this;
    }
    template <class F> void for_each_inner_node(F&& f) {
        ...
    }
};

class Leaf: public Node {
    ...
    Leaf* get_leaf() override {
        return this;
    }
    template <class F> void for_each_leaf(F&& f) {
        ...
    }
};

Alternatively I could use dynamic_cast or std::variant or I could store the type inside Node. In the worst-case my code for for_each uses two virtual method calls and one non-virtual method call, but I wonder what the performance of the alternative approaches would be. Is there a name for this kind of idiom and is there a best practice for how to solve it?

eyelash
  • 3,197
  • 25
  • 33
  • Is `F&& f` going to be a functor/lambda/function-pointer? If so, is it not going to take the Node* as a parameter? I'm not understanding why it needs to be virtual and can't just stay in the base class. – super Jan 07 '18 at 14:44
  • @super it is but it is going to take the data that is stored in the leaves as parameter. I have also other use cases in mind like `template void insert(int pos, I first, I last)`. – eyelash Jan 07 '18 at 14:49
  • Maybe the right place for your question is [Code Review](https://codereview.stackexchange.com/help/on-topic). – xskxzr Jan 07 '18 at 16:25

1 Answers1

0

As alternative to template, you might use std::function:

class Node {
public:
    // ...
    virtual ~Node() = default;
    virtual void for_each(std::function<void(/*DataType*/)> f) = 0;
};

class InnerNode : public Node {
    std::vector<std::unique_ptr<Node>> children;
public:
    //...
    void for_each(std::function<void(/*DataType*/)> f) override {
        // ...
    }
};

class Leaf : public Node {
public:
    //...
    void for_each(std::function<void(/*DataType*/)> f) override {
        // ...
    }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302