C++ Riddle - a tale of two members - August-10, 2022

The following code doesn’t compile.
However there are 5 ways mentioned inside, to make it compile.
Can you explain why it fails to compile? And why each of the proposed changes would make it compile?

Link to code in Compiler Explorer

// code below fails to compile, why?
struct A {
    // (a) removing the following line - code compiles
    std::deque<int> d;
    // (b) removing the following line - code compiles
    std::vector<std::unique_ptr<int>> v;
    // (c) adding the following line - code compiles
    // A(A&&) = default; // note: even without 'noexcept'

    // (d) adding the following line - code compiles
    // std::unique_ptr<int> p;

    A() = default;
};

int main() {
  std::vector<A> v;
  // (e) removing the following line - code compiles
  v.emplace_back();
}
2 Likes

Let me take a stab at it. Just thinking out loud.
First, very hard question. Looking at godbolt/CE it looks like some instantiations for example std::queue trigger static_asserts in stl_uninitialized.h . is_constructible is failing. Removing deque will remove this static_assert. Looks like the same is happening with std::vector of unique_ptr<int>. Looks like the same reason. So removing either “fix” it. But I still do not know why removing just one is ok or enough.
Let’s go to the end - the emplace_back() is triggering the whole thing. It somehow tries to make a copy since copy is defined for this class A. Adding A(A&&) making it move only and that somehow makes things OK. the whole stl_uninitialized is satisfied. Maybe the “default” is copy (I see uninitialized_copy in the output) and when we add unique_ptr we make class A move only and now the class is not copyable but only moveable and the static_assert does not trigger anymore.

I didn’t know how to create a table here, so here is a screenshot of me trying to explain this extremely complex and interesting riddle :slight_smile: