Можно ли определить дополнительный итератор для функции, которая будет изменять поведение функции в зависимости от ее присутствия или нет?
Чтобы привести конкретный пример, рассмотрим определение
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
будет некоторое значение, которое указывает, что пользователь не хочет этот вывод.
Обходной путь для этого путем определения двух отдельных функций, одной с границей и исследуемой в определении, другой без нее, не будет хорошей альтернативой, поскольку для этого потребуется дублирование кода (поскольку логика в функции тесно связана с будь либо frontier
или же explored
настоящее.)
Есть ли какой-то шаблон или замена null_iterator
сделать этот тип кода реализуемым в C ++?
Самое простое решение — написать простой 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; }
};
Можно ли определить дополнительный итератор для функции, которая будет изменять поведение функции в зависимости от ее присутствия или нет?
Нет, это невозможно. Есть два кандидата;
Благодаря комментарию 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>() );
Затем внутри кода функции можно проверить, frontier
или же explored
определяются с
if ( frontier.is_initialized() ) {
} else {
std::cout << "frontier is uninitialized!" << std::endl;
}
if ( explored.is_initialized() ) {
} else {
std::cout << "explored is uninitialized!" << std::endl;
}