【问题标题】:Passing an optional iterator to a function将可选迭代器传递给函数
【发布时间】:2013-06-03 03:57:59
【问题描述】:

是否可以为函数定义一个可选的迭代器,该迭代器将根据函数的存在与否来改变函数的行为?

举个具体的例子,考虑一下定义

template<typename Graph,
    typename random_access_iterator_distances,                        
    typename random_access_iterator_predecessors,                     
    typename back_insertor_iterator_frontier,                         
    typename back_insertor_iterator_explored >
    void dijkstra(const Graph &g,                                     
            const typename boost::graph_traits < Graph >::vertex_descriptor source,    
            random_access_iterator_distances   distances,             
            random_access_iterator_predecessors predecessors,         
            const typename boost::graph_traits < Graph >::vertex_descriptor target = -1,
            back_inserter_iterator_frontier frontier = null_iterator,                 
            back_inserter_iterator_explored explored = null_iterator );

null_iterator 是表示用户不想要此输出的某个值。

通过定义两个单独的函数来解决这个问题,一个有边界并在定义中探索,另一个没有它,这不是一个好的选择,因为它需要重复代码(因为函数中的逻辑是与是否存在 frontierexplored 紧密结合。)

是否有某种模式或替换 null_iterator 以使这种类型的代码可以在 C++ 中实现?

【问题讨论】:

  • boost::optional 怎么样?
  • @KerrekSB:你能详细说明一下吗,这看起来是一个很有前途的解决方案?
  • 好吧,把函数参数设为boost::optional&lt;my_iterator_type&gt;,也许是默认的,然后函数可以检查是否传递了迭代器。

标签: c++ optional-parameters optional-arguments


【解决方案1】:

最简单的解决方案是编写一个简单的 DevNullIterator。因为它是 operator* 没有做任何事情,所以它被简单地内联并编译掉。

struct DevNull {
  template<typename T> operator=(T const&) { }
  template<typename T> operator T&() { static T dummy; return dummy; }
};

struct DevNullIterator {
  DevNull operator*() const { return DevNull();}
  DevNullIterator operator++() const { return *this; }
};

【讨论】:

  • 然后一些算法可能能够通过模板特化对DevNullIterator 类型进行进一步优化,例如,跳过一些只需要填充该参数的输出的函数调用。
【解决方案2】:

感谢 KeresSB 的评论,我最终想出了一个我认为是干净的解决方案。本质上,我使用以下模式:

typedef struct _undefinded {
}undefined_t;

template<typename Graph,
    typename random_access_iterator_distances,
    typename random_access_iterator_predecessors,
    typename back_inserter_iterator_frontier = undefined_t,
    typename back_inserter_iterator_explored = undefined_t >
    void dijkstra(const Graph &g,
            const typename boost::graph_traits < Graph >::vertex_descriptor source,
            random_access_iterator_distances   distances,
            random_access_iterator_predecessors predecessors,
            const typename boost::graph_traits < Graph >::vertex_descriptor target = -1,
            boost::optional<back_inserter_iterator_frontier> frontier = boost::optional<back_inserter_iterator_frontier>(),
            boost::optional<back_inserter_iterator_explored> explored = boost::optional<back_inserter_iterator_explored>() );

然后在函数的代码中,可以检查frontierexplored是否定义了

        if ( frontier.is_initialized() ) {
        } else {
            std::cout << "frontier is uninitialized!" << std::endl;   
        }
        if ( explored.is_initialized() ) {
        } else {
            std::cout << "explored is uninitialized!" << std::endl;   
        }

【讨论】:

  • 第一个 typedef 是 C-ism。
【解决方案3】:

是否可以为函数定义一个可选的迭代器,该迭代器将根据函数的存在与否来改变函数的行为?

没有。这不可能。有两个候选人;

  1. 过载。重载会创建一个新函数(同名),因此这不能满足您的需求。
  2. 默认参数。无法区分参数是来自函数的用户还是来自默认值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-26
    • 2013-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-02
    • 1970-01-01
    • 2015-04-15
    相关资源
    最近更新 更多