J’ai écrit un ensemble de classes pour permettre une fonction zip
simple, de type python. L’extrait suivant fonctionne (presque) exactement comme prévu. Cependant, les deux variables a
et b
ne sont pas const
.
std::vector v1{0.0, 1.1, 2.2, 3.3}; std::vector 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; }
J’ai utilisé gcc 7.3.0. Voici le MCVE:
#include #include #include template class zip_iterator { using value_iterator_type = std::tuple<decltype( std::begin(std::declval()))...>; using value_type = std::tuple<decltype(*std::begin(std::declval()))...>; using Indices = std::make_index_sequence; value_iterator_type i; template value_type dereference(std::index_sequence) { return value_type{*std::get(i) ...}; } public: zip_iterator(value_iterator_type it) : i(it) {} value_type operator*() { return dereference(Indices{}); } }; template class zipper { using Indices = std::make_index_sequence; std::tuple values; template zip_iterator beginner(std::index_sequence) { return std::make_tuple(std::begin(std::get(values)) ...); } public: zipper(Ts& ... args) : values{args...} {} zip_iterator begin() { return beginner(Indices{}); } }; template zipper zip(Ts& ... args) { return {args...}; } int main() { std::vector 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; }
Vous avez un tuple de référence, ce qui signifie que la référence elle-même sera qualifiée de const
(ce qui est mal formé mais ignorée dans ce contexte), pas la valeur référencée par celle-ci.
int a = 7; std::tuple tuple = a; const auto&[aa] = tuple; aa = 9; // ok
Si vous regardez comment std::get
est défini, vous verrez qu’il renvoie const std::tuple_element<0, std::tuple
pour la liaison structurée ci-dessus. Comme le premier tuple est une référence, const&
n’a aucun effet et vous pouvez donc modifier la valeur de retour.
En réalité, c’est la même chose si vous avez un pointeur de classe / un membre de référence que vous pouvez modifier dans une fonction de membre qualifié const
(la valeur pointée / référencée).