7

Unordered set keys are read only, so why in this case I can erase element:

std::unordered_set<std::string> s;
s.emplace("sth");
s.erase("sth");

and in this not:

std::unordered_set<std::string const> s;
const std::string str("sth");
s.emplace(str);
s.erase(str);

If set itself would be const It would make sense but with const keys I don't quite understand that. This assertion fails:

static_assert(!is_reference<_Tp>::value && !is_const<_Tp>::value, "");

Why would somebody who wrote that assertion, check if key is not const?

EDIT:

In fact, the code above compiles fine for std::set. For std::unordered_set, the failure is directly at instantiation. A minimal example to reproduce:

// define a customized hash ... 
int main() { sizeof(std::unordered_set<int const>); }
llllllllll
  • 16,169
  • 4
  • 31
  • 54
Mateusz Wojtczak
  • 1,621
  • 1
  • 12
  • 28
  • Doesn't compile with VS2015 - no hash for `const string`. Anyway, you can't modify set members so why would you declare them const in the first place ? – Sid S Mar 24 '18 at 18:39
  • 1
    I added [tag:language-lawyer], seeing as how the question is asking whether `std::unordered_set` should behave like this or not. Hopefully it aligns with what you originally meant – Passer By Mar 24 '18 at 18:39
  • Are the keys read-only? Or is it just the iterators that are read-only? – Galik Mar 24 '18 at 18:45
  • Providing a hash function to VS2015, I get this error message: "error C2338: The C++ Standard forbids containers of const elements because allocator is ill-formed." So there. – Sid S Mar 24 '18 at 18:48
  • All iterators in a set point to const elements anyway. There's no need I can think of to explicitly use `const T` instead of `T`. – WorldSEnder Mar 24 '18 at 18:56
  • I wonder if the introduction of `std::launder` will allow a relaxation of this requirement Allocator-aware containers. – AndyG Mar 24 '18 at 19:13
  • @WorldSEnder: Immutability and const aren't quite the same. – Lightness Races in Orbit Mar 24 '18 at 19:22
  • 1
    @AndyG [This](https://stackoverflow.com/a/39652132/4832499) pretty much says: no. – Passer By Mar 24 '18 at 19:34
  • Possible duplicate of [Does C++11 allow vector?](https://stackoverflow.com/questions/6954906/does-c11-allow-vectorconst-t) – llllllllll Mar 24 '18 at 20:31

2 Answers2

1

The reason that you cannot erase the element is not because of const-correctness.

It is because you cannot have a container of const things. It is not permitted.

You broke the contract of unordered_set.

The static_assert detected that.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

I think the thing that you're missing is that std::set actually allocates its own strings. so when you say s.emplace("sth"), it creates a new "sth" string, it doesn't use yours (it uses it to construct a new one). Why are these newly allocated strings const ? Because you're not suppose to modify them directly, or else you'll break the set. If you change "aaa" to "zzz" directly then the set will still think that "zzz" is the first element in the set, before "bbb".

So why is that assert there ? Because std does not have an allocator for const objects - so when it tries to allocate a const object it will fail. he VS2017 error is more obvious: "The C++ Standard forbids containers of const elements because allocator is ill-formed."

AEX
  • 69
  • 7