2016-06-14 6 views
1

Solution раздел в gnzlbg «с SO вопрос boost::range::join for multiple ranges подразумевает, что может присоединиться ко многим диапазонов в один клиентский код вызова пользовательской функции VARIADIC шаблон, который вызывает boost::join и boost::make_iterator_range , В соответствии с этим вопросом, ответом и комментариями, предыдущий может присоединиться к 2 диапазонам, и последний необходим для обеспечения перегрузки прежнего уровня не const. Любые контейнеры после второго, предположительно, совершенны, переадресованы через std::forward. Но мой клиентский код может только успешно называть его максимум тремя аргументами. Все, что еще не удается скомпилировать. Что случилось и как это исправить? И есть ли теперь объект Boost, который объединяет многие диапазоны?подталкивание :: Диапазон :: присоединиться много диапазонов в одном пользовательском вызове

Я скопировал и вставил, что реализация OP, в редактировании его здесь только для пробельных читаемости и добавив соответствующие заголовки:

#include <utility> 
#include <boost/range/join.hpp> 

template<class C> 
auto join(C&& c) 
-> decltype(boost::make_iterator_range(std::begin(c), std::end(c))) 
{ 
    return boost::make_iterator_range(std::begin(c), std::end(c)); 
} 

template<class C, class D, class... Args> 
auto join(C&& c, D&& d, Args&&... args) 
-> decltype 
(
    boost::join 
    (
     boost::join 
     (
      boost::make_iterator_range(std::begin(c), std::end(c)), 
      boost::make_iterator_range(std::begin(d), std::end(d)) 
     ), 
     join(std::forward<Args>(args)...) 
    ) 
) 
{ 
    return boost::join 
    (
     boost::join 
     (
      boost::make_iterator_range(std::begin(c), std::end(c)), 
      boost::make_iterator_range(std::begin(d), std::end(d)) 
     ), 
     join(std::forward<Args>(args)...) 
    ); 
} 

и добавил свой код клиента:

#include <deque> 
#include <array> 
#include <vector> 
#include <iostream> 

int main() 
{ 
    std::deque<int> deq { 0, 1, 2, 3, 4 }; 
    std::array<int, 4> stl_arr { 5, 6, 7, 8 }; 
    int c_arr[3] { 9, 10, 11 }; 
    std::vector<int> vec { 12, 13 }; 

    for (auto& i : join(deq, stl_arr, c_arr)) 
    { 
     ++i; 
     std::cout << i << ", ";   // OK, prints 1 thru 12 
    } 

    //join(deq, stl_arr, c_arr, vec); // COMPILER ERROR 
} 

ответ

5

Есть два все происходит. Во-первых, следующие декларации не будет работать, как предполагалось:

template<class C> 
auto join(C&& c) 
-> decltype(boost::make_iterator_range(std::begin(c), std::end(c))); 

template<class C, class D, class... Args> 
auto join(C&& c, D&& d, Args&&... args) 
-> decltype 
(
    boost::join 
    (
     boost::join 
     (
      boost::make_iterator_range(std::begin(c), std::end(c)), 
      boost::make_iterator_range(std::begin(d), std::end(d)) 
     ), 
     join(std::forward<Args>(args)...) 
    // ^^^^-- (1) 
    ) 
); 

Суть дела в том, что на месте (1) вторая перегрузка join не находится в области видимости. С тремя аргументами проблема не возникает, потому что пакет Args имеет длину 1, поэтому в результате расширения join(std::forward<Arg0>(arg0)) используется первая перегрузка, которая равна.

С четырьмя аргументами или более итоговое расширение join(std::forward<Arg0>(arg0), ..., std::forward<ArgN>(argN)) нуждается во второй перегрузке, но оно не находится в области видимости внутри того же типа возврата.

Один из способов исправить это, чтобы включить набор шаблонов функций в набор шаблонов функций-членов, поскольку класс сфера является более мягким:

struct join_type { 
    template<class C> 
    auto operator()(C&& c) const 
    -> decltype(boost::make_iterator_range(std::begin(c), std::end(c))) 
    { 
     return boost::make_iterator_range(std::begin(c), std::end(c)); 
    } 

    template<class C, class D, class... Args> 
    auto operator()(C&& c, D&& d, Args&&... args) const 
    -> decltype 
    (
     boost::join 
     (
      boost::join 
      (
       boost::make_iterator_range(std::begin(c), std::end(c)), 
       boost::make_iterator_range(std::begin(d), std::end(d)) 
      ), 
      (*this)(std::forward<Args>(args)...) 
     ) 
    ) 
    { 
     return boost::join 
     (
      boost::join 
      (
       boost::make_iterator_range(std::begin(c), std::end(c)), 
       boost::make_iterator_range(std::begin(d), std::end(d)) 
      ), 
      (*this)(std::forward<Args>(args)...) 
     ); 
    } 
}; 

constexpr join_type join {}; 

Обратите внимание, что значение имеет класс сфера, не то, что мы решили использовать operator() в качестве имени наших шаблонов функций-членов. Вы также можете использовать шаблоны статических элементов-членов с именем foo и иметь обычный шаблон функции вне класса, который пересылает его.


Теперь мы можем выявить вторую проблему в том, что реализация глючит и будет работать только с нечетным числом аргументов! Даже join(a, b) не будет работать, но эта ошибка могла быть скрыта ранее ADL (то есть клиенты могли бы эффективно вызвать boost::join(a, b), который, очевидно, работает) (Live On Coliru).

Давайте перепишем раз:

struct join_type { 
    template<class C> 
    auto operator()(C&& c) const 
    -> decltype(boost::make_iterator_range(begin(c), end(c))) 
    { 
     return boost::make_iterator_range(begin(c), end(c)); 
    } 

    template<typename First, typename Second, typename... Rest> 
    auto operator()(First&& first, Second&& second, Rest&&... rest) const 
    -> decltype((*this)(boost::join(boost::make_iterator_range(begin(first), end(first)), boost::make_iterator_range(begin(second), end(second))), std::forward<Rest>(rest)...)) 
    { 
     return (*this)(boost::join(boost::make_iterator_range(begin(first), end(first)), boost::make_iterator_range(begin(second), end(second))), std::forward<Rest>(rest)...); 
    } 
}; 

constexpr join_type join {}; 

Live On Coliru