Fedor
Fedor

Reputation: 21307

Why const by-value structural binding allows the user to modify referenced variable?

I am rather confused with structural binding. Even if I use const and by-value (without &) structural binding as in this example:

#include <iostream>
#include <tuple>

int main()
{
    int x = 0;
    std::tuple<int&> p(x);
    const auto [a] = p;
    a++;
    std::cout << x << '\n';
}

It still modifies x and prints 1: https://gcc.godbolt.org/z/jc8hxE8M6

Could you please explain why it works that way and want changes if I add & before [a] or remove const?

Upvotes: 5

Views: 97

Answers (2)

Vlad from Moscow
Vlad from Moscow

Reputation: 311088

The compiler deduces the type similar to the following

typedef int & Ref;

It is the element type of the std::tuple object.

If you will then write for example

int x = 0;
const Ref rx = x; 

then the last declaration is equivalent to

int & const rx = x;

But opposite to pointers references may not be constant in this sense. So the qualifier const is just ignored and you have

int & rx = x;

Here is a demonstrative program.

#include <tuple>
#include <type_traits>

int main()
{
    int x = 0;

    std::tuple<int &> p( x );
    const auto [a] = p;
    std::cout << std::is_same_v<decltype( a ), int &> << '\n';

    std::tuple<int *> p1( &x );
    const auto [b] = p1;
    std::cout << std::is_same_v<decltype( b ), int * const> << '\n';
}

The program output is

1
1

Upvotes: 1

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 123104

See the example on cppreference:

The portion of the declaration preceding [ applies to the hidden variable e, not to the introduced identifiers.

int a = 1, b = 2;
const auto& [x, y] = std::tie(a, b); // x and y are of type int&
auto [z, w] = std::tie(a, b);        // z and w are still of type int&
assert(&z == &a);                    // passes

Where e refers to

A structured binding declaration first introduces a uniquely-named variable (here denoted by e) to hold the value of the initializer, as follows: [...]

The const in your example does not apply to a, why it can still be useful is demonstrated by the next example:

int a = 1;
const auto& [x] = std::make_tuple(a); // OK, not dangling
auto&       [y] = std::make_tuple(a); // error, cannot bind auto& to rvalue std::tuple
auto&&      [z] = std::make_tuple(a); // also OK

Upvotes: 6

Related Questions