11

We had this situation and wondered about the best way to fix it

template<typename T>
struct A : T {
  A(T &&t) noexcept(noexcept(T(std::move(t))))
     :T(std::move(t))
  { }
};

This unfortunately fails to compile because T's move constructor is protected, and we are only allowed to call it in the constructor initialization list for *this. What are the workarounds to make this work or is there even a standard way for it?

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • “and we are only allowed to call it in the constructor initialization list for `*this`” — wait, what? Is this a special rule for protected *constructors*? That you can only use them in the initialisation list? – Konrad Rudolph Aug 04 '16 at 16:18
  • Which compiler fails to compile it? – ecatmur Aug 04 '16 at 16:20
  • 1
    @ecatmur Intel/GCC fails (Clang probably aswell). As with all protected functions, only calls through objects of the type of `*this` or derived classes are permitted. – Johannes Schaub - litb Aug 04 '16 at 16:23
  • @JohannesSchaub-litb So what you’re stumbling over here is the lack of a dedicated syntax to call the constructor for `this`? This looks like a pretty bad oversight. Is using placement-new in `noexcept` legal (and even if it’s legal: does it propagate the `noexcept`ness of the constructor it calls)? – Konrad Rudolph Aug 04 '16 at 16:25
  • @KonradRudolph we ended up creating a `struct Dummy : T { Dummy(T &&t):T(std::move(t)) { } };` and doing `noexcept(Dummy(std::move(t)))` in the ctor of `A`, but it strikes me as very ugly and we wonder about a better way. – Johannes Schaub - litb Aug 04 '16 at 16:29
  • 1
    @JohannesSchaub-litb I don’t understand why this works: doesn’t `Dummy`’s constructor have the same problem? Who don’t you need to declare it as `noexcept`? Does the compiler infer it here? Then why doesn’t it infer it for `A`? – Konrad Rudolph Aug 04 '16 at 16:31
  • 2
    @KonradRudolph ah you are right, I think we did instead use the implicitly generated move constructor of `Dummy`. Since it ended up calling `T::T(T&&)` aswell and deduces the noexceptedness automatically. – Johannes Schaub - litb Aug 04 '16 at 16:34
  • @JohannesSchaub-litb Ah, that makes sense. I think this is a sensible solution if you make `Dummy` a template and name it properly, maybe with an appropriate wrapper function, e.g. `noexcept(noexcept(has_noexcept_move_constructor()))`. That should work, shouldn’t it? – Konrad Rudolph Aug 04 '16 at 16:37
  • @KonradRudolph I think so, but perhaps there's something else? Is it a known defect that C++17 fixes with something better? `noexcept(auto)` perhaps? – Johannes Schaub - litb Aug 04 '16 at 16:41
  • Wait a second, shouldn't this just work according to [class.access]/6? – bogdan Aug 04 '16 at 16:50
  • 1
    OK, it looks like the intent is to have [class.protected] apply, but the wording is not quite there yet - [CWG1883](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1883). – bogdan Aug 04 '16 at 17:29

1 Answers1

1

you are looking for noexcept(std::is_nothrow_move_constructible<T>::value): http://en.cppreference.com/w/cpp/types/is_move_constructible

Andrei R.
  • 2,374
  • 1
  • 13
  • 27