A minimized example of my code showing the problem:
#include <cassert>
#include <iostream>
#include <map>
#include <string>
template <typename T>
const std::map<std::string, T> smap;
template <>
const std::map<std::string, bool> smap<bool>{{"a", false}};
int main() {
std::map<bool, std::string> rmap{{false, "x"}};
for (const auto& [key, val] : rmap) {
std::cerr << typeid(bool).hash_code() << "\n";
std::cerr << typeid(decltype(key)).hash_code() << "\n";
std::cerr << smap<bool>.size() << "\n";
std::cerr << smap<decltype(key)>.size() << "\n";
assert((std::is_same_v<bool, decltype(key)>));
}
return 0;
}
It gives the output:
10838281452030117757
10838281452030117757
1
0
example.cpp:22: int main(): Assertion `(std::is_same_v<bool, decltype(key)>)' failed.
Why is it that I can’t access the variable template using decltype when it’s referring to the same type (bool)?
For the record I also tried to not use structured binding and using decltype on first in the pair with the same result.
However if I create an actual bool variable, like so …
bool b;
std::cerr << settings_map<decltype(b)>.size() << "\n";
… it’s working.
>Solution :
decltype(key) is const bool, not bool. And typeid strips const qualifiers, so the two have the same (runtime) representation.
If the type of type or expression is cv-qualified, the result of the typeid refers to a
std::type_infoobject representing the cv-unqualified type (that is,typeid(const T) == typeid(T)).
So while typeid treats the two types as equivalent, template expansion (and is_same_v) does not, and you get two different maps: one for bool and one for const bool. Note that the assertion
assert((std::is_same_v<const bool, decltype(key)>));
succeeeds if put in place of the one in your code. To remove cv-qualifiers, use remove_cv_t.
std::cerr << settings_map<std::remove_cv_t<decltype(key)>>.size() << "\n";
In your last snippet
bool b;
std::cerr << settings_map<decltype(b)>.size() << "\n";
The b is not constant, so decltype(b) is actually bool, not const bool.