Range-based for loop on unordered_map and references
Clash Royale CLAN TAG#URR8PPP
up vote
9
down vote
favorite
When running a range-based for loop on an std::unordered_map it appears that the type of the loop variable does not use reference types:
std::unordered_map<int, int> map = 0, 1, 1, 2, 2, 3 ;
for(auto&[l, r] : map)
static_assert(std::is_same_v<decltype(r), int&>);
MSVC 2017, gcc 8.2 and clang 7.0.0 all report a failed assertion here. Oppose this to a std::vector, where the assertion does not fail, as one would expect:
std::vector<int> vec = 1, 2, 3 ;
for(auto& r : vec)
static_assert(std::is_same_v<decltype(r), int&>);
However on both MSVC 2017 and gcc 8.2 a loop modifying the local variable r will have observable side-effects:
#include <iostream>
#include <type_traits>
#include <unordered_map>
#include <vector>
int main()
std::unordered_map<int, int> a = 0, 1, 1, 2, 2, 3 ;
for(auto[l, r] : a)
std::cout << l << "; " << r << std::endl;
for(auto&[l, r] : a)
static_assert(std::is_same_v<decltype(r), int>);
r++;
std::cout << "Increment:" << std::endl;
for(auto[l, r] : a)
std::cout << l << "; " << r << std::endl;
This program for example will print (ignoring order):
0; 1
1; 2
2; 3
Increment:
0; 2
1; 3
2; 4
What am I missing?
How can this change the value in the map despite the local variable not being of reference type?
Or probably more appropriately, why does std::is_same not see the right type, because quite clearly it is a reference type?
Or am I alternatively missing some undefined behavior?
Note that I did reproduce the same issue without using structured bindings, so I keep the nice looking code here.
See here for an example
c++ language-lawyer c++17 unordered-map structured-bindings
add a comment |Â
up vote
9
down vote
favorite
When running a range-based for loop on an std::unordered_map it appears that the type of the loop variable does not use reference types:
std::unordered_map<int, int> map = 0, 1, 1, 2, 2, 3 ;
for(auto&[l, r] : map)
static_assert(std::is_same_v<decltype(r), int&>);
MSVC 2017, gcc 8.2 and clang 7.0.0 all report a failed assertion here. Oppose this to a std::vector, where the assertion does not fail, as one would expect:
std::vector<int> vec = 1, 2, 3 ;
for(auto& r : vec)
static_assert(std::is_same_v<decltype(r), int&>);
However on both MSVC 2017 and gcc 8.2 a loop modifying the local variable r will have observable side-effects:
#include <iostream>
#include <type_traits>
#include <unordered_map>
#include <vector>
int main()
std::unordered_map<int, int> a = 0, 1, 1, 2, 2, 3 ;
for(auto[l, r] : a)
std::cout << l << "; " << r << std::endl;
for(auto&[l, r] : a)
static_assert(std::is_same_v<decltype(r), int>);
r++;
std::cout << "Increment:" << std::endl;
for(auto[l, r] : a)
std::cout << l << "; " << r << std::endl;
This program for example will print (ignoring order):
0; 1
1; 2
2; 3
Increment:
0; 2
1; 3
2; 4
What am I missing?
How can this change the value in the map despite the local variable not being of reference type?
Or probably more appropriately, why does std::is_same not see the right type, because quite clearly it is a reference type?
Or am I alternatively missing some undefined behavior?
Note that I did reproduce the same issue without using structured bindings, so I keep the nice looking code here.
See here for an example
c++ language-lawyer c++17 unordered-map structured-bindings
3
The culprit isdecltype
: "If the argument is an unparenthesized id-expression naming a structured binding, then decltype yields the referenced type". But I admit I have no clue what's the rationale for that.
– HolyBlackCat
44 mins ago
A smaller failing example:std::pair<const int, int> foo1,2;
auto & [first, second] = foo;
static_assert(std::is_same_v<decltype(second), int&>);
– Michael Veksler
36 mins ago
1
@HolyBlackCat to make C++ even more confusing and less consistent. Seems to be the rationale for everything we added in the last years.
– inf
34 mins ago
What are the types of identifiers introduced by structured bindings in C++17?; structured bindings: when something looks like a reference and behaves similarly to a reference, but it's not a reference
– cpplearner
20 mins ago
add a comment |Â
up vote
9
down vote
favorite
up vote
9
down vote
favorite
When running a range-based for loop on an std::unordered_map it appears that the type of the loop variable does not use reference types:
std::unordered_map<int, int> map = 0, 1, 1, 2, 2, 3 ;
for(auto&[l, r] : map)
static_assert(std::is_same_v<decltype(r), int&>);
MSVC 2017, gcc 8.2 and clang 7.0.0 all report a failed assertion here. Oppose this to a std::vector, where the assertion does not fail, as one would expect:
std::vector<int> vec = 1, 2, 3 ;
for(auto& r : vec)
static_assert(std::is_same_v<decltype(r), int&>);
However on both MSVC 2017 and gcc 8.2 a loop modifying the local variable r will have observable side-effects:
#include <iostream>
#include <type_traits>
#include <unordered_map>
#include <vector>
int main()
std::unordered_map<int, int> a = 0, 1, 1, 2, 2, 3 ;
for(auto[l, r] : a)
std::cout << l << "; " << r << std::endl;
for(auto&[l, r] : a)
static_assert(std::is_same_v<decltype(r), int>);
r++;
std::cout << "Increment:" << std::endl;
for(auto[l, r] : a)
std::cout << l << "; " << r << std::endl;
This program for example will print (ignoring order):
0; 1
1; 2
2; 3
Increment:
0; 2
1; 3
2; 4
What am I missing?
How can this change the value in the map despite the local variable not being of reference type?
Or probably more appropriately, why does std::is_same not see the right type, because quite clearly it is a reference type?
Or am I alternatively missing some undefined behavior?
Note that I did reproduce the same issue without using structured bindings, so I keep the nice looking code here.
See here for an example
c++ language-lawyer c++17 unordered-map structured-bindings
When running a range-based for loop on an std::unordered_map it appears that the type of the loop variable does not use reference types:
std::unordered_map<int, int> map = 0, 1, 1, 2, 2, 3 ;
for(auto&[l, r] : map)
static_assert(std::is_same_v<decltype(r), int&>);
MSVC 2017, gcc 8.2 and clang 7.0.0 all report a failed assertion here. Oppose this to a std::vector, where the assertion does not fail, as one would expect:
std::vector<int> vec = 1, 2, 3 ;
for(auto& r : vec)
static_assert(std::is_same_v<decltype(r), int&>);
However on both MSVC 2017 and gcc 8.2 a loop modifying the local variable r will have observable side-effects:
#include <iostream>
#include <type_traits>
#include <unordered_map>
#include <vector>
int main()
std::unordered_map<int, int> a = 0, 1, 1, 2, 2, 3 ;
for(auto[l, r] : a)
std::cout << l << "; " << r << std::endl;
for(auto&[l, r] : a)
static_assert(std::is_same_v<decltype(r), int>);
r++;
std::cout << "Increment:" << std::endl;
for(auto[l, r] : a)
std::cout << l << "; " << r << std::endl;
This program for example will print (ignoring order):
0; 1
1; 2
2; 3
Increment:
0; 2
1; 3
2; 4
What am I missing?
How can this change the value in the map despite the local variable not being of reference type?
Or probably more appropriately, why does std::is_same not see the right type, because quite clearly it is a reference type?
Or am I alternatively missing some undefined behavior?
Note that I did reproduce the same issue without using structured bindings, so I keep the nice looking code here.
See here for an example
c++ language-lawyer c++17 unordered-map structured-bindings
c++ language-lawyer c++17 unordered-map structured-bindings
edited 33 mins ago


Swordfish
5,7851932
5,7851932
asked 53 mins ago


Hanno Bänsch
9826
9826
3
The culprit isdecltype
: "If the argument is an unparenthesized id-expression naming a structured binding, then decltype yields the referenced type". But I admit I have no clue what's the rationale for that.
– HolyBlackCat
44 mins ago
A smaller failing example:std::pair<const int, int> foo1,2;
auto & [first, second] = foo;
static_assert(std::is_same_v<decltype(second), int&>);
– Michael Veksler
36 mins ago
1
@HolyBlackCat to make C++ even more confusing and less consistent. Seems to be the rationale for everything we added in the last years.
– inf
34 mins ago
What are the types of identifiers introduced by structured bindings in C++17?; structured bindings: when something looks like a reference and behaves similarly to a reference, but it's not a reference
– cpplearner
20 mins ago
add a comment |Â
3
The culprit isdecltype
: "If the argument is an unparenthesized id-expression naming a structured binding, then decltype yields the referenced type". But I admit I have no clue what's the rationale for that.
– HolyBlackCat
44 mins ago
A smaller failing example:std::pair<const int, int> foo1,2;
auto & [first, second] = foo;
static_assert(std::is_same_v<decltype(second), int&>);
– Michael Veksler
36 mins ago
1
@HolyBlackCat to make C++ even more confusing and less consistent. Seems to be the rationale for everything we added in the last years.
– inf
34 mins ago
What are the types of identifiers introduced by structured bindings in C++17?; structured bindings: when something looks like a reference and behaves similarly to a reference, but it's not a reference
– cpplearner
20 mins ago
3
3
The culprit is
decltype
: "If the argument is an unparenthesized id-expression naming a structured binding, then decltype yields the referenced type". But I admit I have no clue what's the rationale for that.– HolyBlackCat
44 mins ago
The culprit is
decltype
: "If the argument is an unparenthesized id-expression naming a structured binding, then decltype yields the referenced type". But I admit I have no clue what's the rationale for that.– HolyBlackCat
44 mins ago
A smaller failing example:
std::pair<const int, int> foo1,2;
auto & [first, second] = foo;
static_assert(std::is_same_v<decltype(second), int&>);
– Michael Veksler
36 mins ago
A smaller failing example:
std::pair<const int, int> foo1,2;
auto & [first, second] = foo;
static_assert(std::is_same_v<decltype(second), int&>);
– Michael Veksler
36 mins ago
1
1
@HolyBlackCat to make C++ even more confusing and less consistent. Seems to be the rationale for everything we added in the last years.
– inf
34 mins ago
@HolyBlackCat to make C++ even more confusing and less consistent. Seems to be the rationale for everything we added in the last years.
– inf
34 mins ago
What are the types of identifiers introduced by structured bindings in C++17?; structured bindings: when something looks like a reference and behaves similarly to a reference, but it's not a reference
– cpplearner
20 mins ago
What are the types of identifiers introduced by structured bindings in C++17?; structured bindings: when something looks like a reference and behaves similarly to a reference, but it's not a reference
– cpplearner
20 mins ago
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
6
down vote
Structured bindings are modeled as aliases, not "real" references. Even though they may use a reference under the hood.
Imagine that you have
struct X
const int first = 0;
int second;
int third : 8;
;
X x;
X& y = x;
What's decltype(x.second)
? int
. What's decltype(y.second)
? int
. And so in
auto& [first, second, third] = x;
decltype(second)
is int
, because second
is an alias for x.second
. And third
poses no problems even though it's not allowed to bind a reference to a bit-field, because it's an alias, not an actual reference.
The tuple-like case is designed to be consistent with that. Even though in that case the language has to use references, it does its best to pretend that those references do not exist.
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
Structured bindings are modeled as aliases, not "real" references. Even though they may use a reference under the hood.
Imagine that you have
struct X
const int first = 0;
int second;
int third : 8;
;
X x;
X& y = x;
What's decltype(x.second)
? int
. What's decltype(y.second)
? int
. And so in
auto& [first, second, third] = x;
decltype(second)
is int
, because second
is an alias for x.second
. And third
poses no problems even though it's not allowed to bind a reference to a bit-field, because it's an alias, not an actual reference.
The tuple-like case is designed to be consistent with that. Even though in that case the language has to use references, it does its best to pretend that those references do not exist.
add a comment |Â
up vote
6
down vote
Structured bindings are modeled as aliases, not "real" references. Even though they may use a reference under the hood.
Imagine that you have
struct X
const int first = 0;
int second;
int third : 8;
;
X x;
X& y = x;
What's decltype(x.second)
? int
. What's decltype(y.second)
? int
. And so in
auto& [first, second, third] = x;
decltype(second)
is int
, because second
is an alias for x.second
. And third
poses no problems even though it's not allowed to bind a reference to a bit-field, because it's an alias, not an actual reference.
The tuple-like case is designed to be consistent with that. Even though in that case the language has to use references, it does its best to pretend that those references do not exist.
add a comment |Â
up vote
6
down vote
up vote
6
down vote
Structured bindings are modeled as aliases, not "real" references. Even though they may use a reference under the hood.
Imagine that you have
struct X
const int first = 0;
int second;
int third : 8;
;
X x;
X& y = x;
What's decltype(x.second)
? int
. What's decltype(y.second)
? int
. And so in
auto& [first, second, third] = x;
decltype(second)
is int
, because second
is an alias for x.second
. And third
poses no problems even though it's not allowed to bind a reference to a bit-field, because it's an alias, not an actual reference.
The tuple-like case is designed to be consistent with that. Even though in that case the language has to use references, it does its best to pretend that those references do not exist.
Structured bindings are modeled as aliases, not "real" references. Even though they may use a reference under the hood.
Imagine that you have
struct X
const int first = 0;
int second;
int third : 8;
;
X x;
X& y = x;
What's decltype(x.second)
? int
. What's decltype(y.second)
? int
. And so in
auto& [first, second, third] = x;
decltype(second)
is int
, because second
is an alias for x.second
. And third
poses no problems even though it's not allowed to bind a reference to a bit-field, because it's an alias, not an actual reference.
The tuple-like case is designed to be consistent with that. Even though in that case the language has to use references, it does its best to pretend that those references do not exist.
answered 22 mins ago
T.C.
102k13207311
102k13207311
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52914597%2frange-based-for-loop-on-unordered-map-and-references%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
3
The culprit is
decltype
: "If the argument is an unparenthesized id-expression naming a structured binding, then decltype yields the referenced type". But I admit I have no clue what's the rationale for that.– HolyBlackCat
44 mins ago
A smaller failing example:
std::pair<const int, int> foo1,2;
auto & [first, second] = foo;
static_assert(std::is_same_v<decltype(second), int&>);
– Michael Veksler
36 mins ago
1
@HolyBlackCat to make C++ even more confusing and less consistent. Seems to be the rationale for everything we added in the last years.
– inf
34 mins ago
What are the types of identifiers introduced by structured bindings in C++17?; structured bindings: when something looks like a reference and behaves similarly to a reference, but it's not a reference
– cpplearner
20 mins ago