Référence de liaison structurée avec tuple de référence

Oct 02 2020

Le cas de liaison structurée 2 dans cppreference est un peu difficile à comprendre. En gros, je veux une clarification de ces situations

int x = 1;
double y = 2.0;
auto [a, b] = std::forward_as_tuple(x, y);   //a, b are both reference, why?
auto&& [c, d] = std::forward_as_tuple(x, y); //What's the difference of this and above?
auto&& [e, f] = std::tuple{x, y};  //why are NOT e, f rvalue references? Resharper shows them as value type not reference type

Et s'il existe un tuple de référence de retour de fonction, comment puis-je faire une copie à l'aide d'une liaison structurée?

std::tuple<int&, double&> f;
auto [x, y] = f(); //But I want a copy from the reference, how?

Réponses

3 Barry Oct 02 2020 at 15:42

std::forward_as_tuple(x, y)vous donne un tuple<int&, double&>. Les types de liaisons dans qui sont int&et double&(de la même manière que les types de liaisons dans tuple<int, double>sont intet double). Fondamentalement:

auto [a, b] = std::forward_as_tuple(x, y);
auto&& [c, d] = std::forward_as_tuple(x, y);

se comporte comme si:

auto __e = std::forward_as_tuple(x, y);
using __E = remove_reference_t<decltype(__e)>;
tuple_element_t<0, __E>&& a = std::get<0>(std::move(__e));
tuple_element_t<1, __E>&& b = std::get<1>(std::move(__e));

auto&& __f = std::forward_as_tuple(x, y);
using __F = remove_reference_t<decltype(__f)>;
tuple_element_t<0, F>&& c = std::get<0>(std::move(__f));
tuple_element_t<1, F>&& d = std::get<1>(std::move(__f));

Ainsi aest une référence rvalue à int&et cest une référence rvalue à double&, so int&et double&respectivement. Cette formulation particulière (je l'appelle spécifiquement une référence à une référence, plutôt que de l'appeler simplement int&) est nécessaire car decltype(name)où nameest une liaison structurée vous donne le type référencé , c'est pourquoi decltype(a)vous le donnerait int&.

Ce qui précède montre également la différence entre le [a, b]et le [c, d]cas: la déclaration autovs auto&&s'applique à l'objet sans nom que nous déstructurons. Cela n'affecte pas les liaisons elles-mêmes † .

Ce cas:

auto&& [e, f] = std::tuple{x, y};

Ne donne pas de références car il décompresse:

auto&& __g = std::tuple{x, y};
using __G = remove_reference_t<decltype(__g)>;
tuple_element_t<0, G>&& e = std::get<0>(std::move(__g));
tuple_element_t<1, G>&& f = std::get<1>(std::move(__g));

Donc , eest une référence à rvalue int, ce qui signifie decltype(e)est int, non int&.


Et s'il existe un tuple de référence de retour de fonction, comment puis-je faire une copie à l'aide d'une liaison structurée?

Vous ne pouvez pas faire de copie à l'aide de liaisons structurées. Les liaisons structurées consistent uniquement à déstructurer un objet, il ne s'agit pas du tout de changer quoi que ce soit. Si vous souhaitez faire une copie, vous devez le faire manuellement:

std::tuple<int&, double&> f = /* ... */;
std::tuple<int, double> actual_copy = f;
auto& [x, y] = actual_copy; 

† Dans le cas ci-dessus, parce que l'objet sous-jacent en cours de déstructuration est une référence lvalue ( auto&), cela rend techniquement les liaisons elles-mêmes des références lvalue à tout au lieu de références rvalue à quoi que ce soit - bien que je ne sois pas sûr que ce soit réellement une distinction significative .

1 eerorika Oct 02 2020 at 15:44

s'il y a un tuple de référence de retour de fonction, comment puis-je faire une copie en utilisant une liaison structurée?

Peut-être qu'une fonction d'assistance telle que la suivante serait utile:

template <class Tuple, size_t... indices>
constexpr auto
tuple_copy_impl(const Tuple& tuple, std::index_sequence<indices...>) {
   return std::tuple{std::get<indices>(tuple)...};
}

template <class Tuple>
constexpr auto
tuple_copy(const Tuple& tuple) {
    constexpr auto s = std::tuple_size_v<Tuple>;
    using I = std::make_index_sequence<s>;
    return tuple_copy_impl<Tuple>(tuple, I{});
}

auto [x, y] = tuple_copy(f());