11

Why does std::runtime_error not provide a constructor accepting an std::string&&? Looking at the constructors for std::string, it has a move constructor, but the noexcept specification is only there for C++14, not C++11. Was this a mistake, a deadline that was missed or am I missing something?

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
rwols
  • 2,968
  • 2
  • 19
  • 26
  • If `std::runtime_error` had a constructor taking `std::string&&`, it certainly would not be a move constructor. A move constructor would take `std::runtime_error&&`. – Lightness Races in Orbit Jan 18 '15 at 19:16
  • I'm not sure I follow: judging from the beginning it seems your question is about `runtime_error`, but then you switch to `std::string`. What point are you trying to make? – Andy Prowl Jan 18 '15 at 19:21
  • @AndyProwl: `std::runtime_error` may presently be constructed from a `const std::string&` or from a `const char* what_arg`. He's asking why it cannot be constructed from a `std::string&&`. – Lightness Races in Orbit Jan 18 '15 at 19:22
  • @LightnessRacesinOrbit: Yes, but he seems to be making a point about exception-safety by mentioning the `noexcept` qualification of `string`'s move constructor, which I'm not entirely following. – Andy Prowl Jan 18 '15 at 19:25
  • 2
    Yes, I'm sorry if the question caused confusion. The question is why `std::runtime_error(std::string&&)` is not present. – rwols Jan 18 '15 at 19:55

1 Answers1

17

explicit runtime_error(string&&);

does not exist simply because it would not provide any optimization.

As it turns out, a C++11-conforming runtime_error does not internally store a std::string. The reason is that the copy members of runtime_error must not throw exceptions. Otherwise the wrong exception could get thrown when the compiler copies the exception object in the process of throwing it.

This implies that runtime_error needs to store a non-mutable reference counted string. However C++11 outlaws the COW-implementation for std::string. Implementations of std::string have moved to a "short-string-optimization" which must allocate on copy construction if the length of the string is beyond the "short limit". And there is no limit on the length of strings used to construct a runtime_error.

So effectively C++11 (and forward) contains two implementations of strings:

  1. std::string : This is typically a short-string-optimized type with a copy constructor and copy assignment that is capable of throwing exceptions.

  2. std::runtime_error : This is (or holds) an immutable reference-counted string. This will never throw on copy construction or copy assignment.

And

explicit runtime_error(string&&);

can never (efficiently) transfer resources from the "type 1" string to the "type 2" string.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    What would the type 2 string look like, out of interest? (If it's a black-boxed, hidden, implementation-defined internal type then that's a sufficient answer.) – Lightness Races in Orbit Jan 18 '15 at 19:36
  • 5
    It could look like the non-mutating parts of a C++03 COW-based `std::string`. Indeed, that is exactly what libc++ did. Its type 2 string has an ABI identical to the gcc-4.2 `std::string` so that `runtime_error` can be thrown from libc++ and caught using libstdc++ (and vice-versa), *within the same application*. – Howard Hinnant Jan 18 '15 at 19:39
  • Why would you want to do that? – Lightness Races in Orbit Jan 18 '15 at 19:40
  • 5
    When libc++ and libstdc++ are dylibs, and when an app is composed of many dylibs, it becomes quite likely that during a transition period on a platform such as OS X, an app will unwittingly, indirectly, link to both libc++ and libstdc++, unless said app can directly control the building of all of the dylibs it uses. – Howard Hinnant Jan 18 '15 at 19:42
  • 4
    Similarly, libstdc++ in GCC5 still uses the old COW `std::string` in its exception types (via an opaque type and some hideous hackery), even when the new SSO `std::string` is the one visible to users. This should support throwing `std::runtime_error` in code using any `std::string` from GCC4, GCC5 or libc++, and catching it in code using a different `std::string`. – Jonathan Wakely Jan 19 '15 at 14:28