Подтвердить что ты не робот

Диапазон для цикла с несколькими контейнерами

Предположим, что у меня есть 2 (или более) контейнера, которые я хочу итерации одновременно, например, для вычисления точечного произведения двух векторов:

std::vector<double> vector1;
std::vector<double> vector2;    // identical size to vector1

Каков предпочтительный способ С++ 11 указать цикл диапазона для обоих (или всех) контейнеров одновременно? Включает ли это выбор одного контейнера/итератора для записи короткого замыкания (т.е. for ( auto i : c )) в цикле диапазона, в то время как все остальные контейнеры/итераторы должны обрабатываться долго? Есть ли причина, по которой синтаксис в будущем не может быть расширен для поддержки коротких рук для обоих/всех контейнеров, как показано ниже... что кажется действительно читаемым:

double dotProduct( 0.0 );
for ( auto const & value1 : vector1, auto const & value2 : vector2 )  // illegal!
{
    dotProduct += value1*value2;
}
4b9b3361

Ответ 1

В других (часто функциональных) языках это делается с помощью функции zip. Например, Python имеет встроенный zip, который выполняет итерацию поверх своих аргументов и возвращает кортеж:

for i in zip( [1,2,3], (1,2,3), { 0:0, 1:1, 2:2 } ): 
    l,t,d = i 
    print("list item: %d, tuple item %d, dict item %d" % (l,t,d) )      

Вы можете использовать библиотеку диапазонов в C++, чтобы получить эту функциональность, например Boost.Range или Eric Niebler rangev3. К сожалению, в стандарте C++ 17 диапазоны, к сожалению, не голосовали, но я бы никогда не начал проект без библиотеки диапазонов. В Boost.Range функция называется combine:

#include <boost/range/combine.hpp>
#include <boost/tuple/tuple.hpp>
#include <iostream>
#include <vector>
#include <list>

int main(int, const char*[])
{
    using namespace boost;

    std::vector<int> const v{0,1,2,3,4};
    std::list<char> const  l{'a', 'b', 'c', 'd', 'e'};

    for(auto const& i: combine(v, l))
    {
        int ti;
        char tc;
        tie(ti,tc) = i;
        std::cout << '(' << ti << ',' << tc << ')' << '\n';
    }

    return 0;
}

С помощью C++ 17 вы можете заменить std::tie на структурированное связывание и удалить необычную "инициализацию" с помощью std::tie.

  for(auto const& [ti,tc] : boost::combine(v, l)) {
     std::cout << '(' << ti << ',' << tv << ')' << '\n';
  }

Хотя я сожалею о том, что диапазоны не включены в C++ 17, я считаю, что структурированные привязки являются большим достижением и серьезно изменят способ написания кода. Наличие диапазонов в стандарте сделает их более популярными и поднимет их из сторонней библиотеки, где у многих людей есть возражения, потому что это то, что они не знают, стандартной функции, которую должен знать программист C++.