Переменные, помеченные как const с использованием структурированных привязок, не являются постоянными
Я писал набор классов, чтобы позволить простой Python-как zip
-функции. Следующий фрагмент работает (почти) так же, как и ожидалось. Тем не менее, две переменные a
а также b
не const
,
std::vector<double> v1{0.0, 1.1, 2.2, 3.3};
std::vector<int> v2{0, 1, 2};
for (auto const& [a, b] : zip(v1, v2))
{
std::cout << a << '\t' << b << std::endl;
a = 3; // I expected this to give a compiler error, but it does not
std::cout << a << '\t' << b << std::endl;
}
Я использую GCC 7.3.0. Вот MCVE:
#include <iostream>
#include <tuple>
#include <vector>
template <class ... Ts>
class zip_iterator
{
using value_iterator_type = std::tuple<decltype( std::begin(std::declval<Ts>()))...>;
using value_type = std::tuple<decltype(*std::begin(std::declval<Ts>()))...>;
using Indices = std::make_index_sequence<sizeof...(Ts)>;
value_iterator_type i;
template <std::size_t ... I>
value_type dereference(std::index_sequence<I...>)
{
return value_type{*std::get<I>(i) ...};
}
public:
zip_iterator(value_iterator_type it) : i(it) {}
value_type operator*()
{
return dereference(Indices{});
}
};
template <class ... Ts>
class zipper
{
using Indices = std::make_index_sequence<sizeof...(Ts)>;
std::tuple<Ts& ...> values;
template <std::size_t ... I>
zip_iterator<Ts& ...> beginner(std::index_sequence<I...>)
{
return std::make_tuple(std::begin(std::get<I>(values)) ...);
}
public:
zipper(Ts& ... args) : values{args...} {}
zip_iterator<Ts& ...> begin()
{
return beginner(Indices{});
}
};
template <class ... Ts>
zipper<Ts& ...> zip(Ts& ... args)
{
return {args...};
}
int main()
{
std::vector<double> v{1};
auto const& [a] = *zip(v).begin();
std::cout << a << std::endl;
a = 2; // I expected this to give a compiler error, but it does not
std::cout << a << std::endl;
}
1 ответ
У вас есть кортеж ссылки, что означает, что сама ссылка будет const
квалифицированный (который неправильно сформирован, но в этом контексте игнорируется), а не значение, на которое он ссылается.
int a = 7;
std::tuple<int&> tuple = a;
const auto&[aa] = tuple;
aa = 9; // ok
Если вы посмотрите, как std::get
определяется, вы увидите, что он возвращает const std::tuple_element<0, std::tuple<int&>>&
для структурированного связывания выше. Поскольку первый элемент кортежа является ссылкой, const&
не имеет никакого эффекта, и, таким образом, вы можете изменить возвращаемое значение.
Действительно, это то же самое, если у вас есть указатель / ссылочный член класса, который вы можете изменить в const
квалифицированная функция-член (значение указано / указано).