2016-09-02 38 views
1

我有类似列表的数据结构的非const版本:定义常量和穿越功能

template<typename T> 
struct node { 
    T val; 
    unique_ptr<node<T>> next; 
}; 

和一个简单的穿越功能:

template<typename T, typename UnaryOp> 
void traverse(node<T>& list, UnaryOp op) { 
    node<T>* current = &list; 
    while(current) { 
    op(*current); 
    current = current->next.get(); 
} 

我现在需要一个constnon-const版本的traverse函数接受const node<T>& listnode<T>& list,这取决于上下文,最好避免代码重复。这怎么可能实现?

+0

这是什么,你需要在这里'const'?遍历函数根本不必是成员,所以你似乎在寻求别的东西。 – thokra

+0

签名中的'list'参数。我需要一个''const节点&list''和一个''节点&list''。一个例子是实现一个函数''size_t size(const节点&list){...}'' – fuji

+0

澄清这个问题,以反映我的意见。 – fuji

回答

1

一个可能的解决方案是创建一个静态模板函数,它*this作为转发参考:

class node 
{ 
private: 
    template <typename TSelf> 
    static void traverse_impl(TSelf& self, node<T>& list, UnaryOp op) 
    { 
     node<T>* current = &list; 
     while(current) 
     { 
      op(*current); 
      current = current->next.get(); 
     } 
    } 

public: 
    void traverse(node<T>& list, UnaryOp op) 
    { 
     traverse_impl(*this, list, op); 
    } 

    void traverse(node<T>& list, UnaryOp op) const 
    { 
     traverse_impl(*this, list, op); 
    } 
}; 

这工作,因为template argument deduction rules - 总之,TSelf都接受const和非const引用。

如果您需要访问traverse_impl中的this的成员,请使用self.member

此外,还可以使用std::conditional或类似设施内traverse_impl取决于const -ness的TSelf做不同的事情。您也可以使用转发参考TSelf&&并处理this正在移动的情况,这要归功于ref-qualifiersperfect-forwarding

+0

解决这个问题有多令人兴奋吗?为什么'traverse_impl'不是一个免费的函数?为什么'遍历'调度到一个潜在的带有'const'和non-'const''的自由函数'TSelf',尽管该函数在任何地方都不使用'self'?这基本上是标签调度,但为什么?它不会以任何方式改变您遍历的方式以及'UnaryOp'对'list'中的节点所做的事情。我错过了什么吗? – thokra

+0

@thokra在OP的例子中,这个解决方案是过度杀伤 - 一个免费的功能也可以工作。我正在回答**更普遍的** *“如何避免const /非const成员重复?”*答案。使用'static'模板函数允许实现在类*(它应该是)*中。使用'TSelf'参数允许实现在需要时访问成员,并通过使用'type_traits'来轻松检查'const'。我相信,尽早尽可能通用,而不是稍后重写大部分代码。 –

+0

这很好,但我不明白“它应该在哪里”的结论 - 为什么?我没有看到它是一个静态成员函数 - 没有任何好处。它也可以是实现文件中一个未命名名称空间中的一个自由函数,因此它完全被“node”和世界其他地方的用户隐藏,而不关心实现。你是通用的,是的,但恕我直言,你也是不必要的冗长和尽可能通用尽可能早,根据我的经验,如果做得不好,可能会非常非常糟糕。 – thokra