2

What's the requirements on the following case in the current C++ Standard?

Suppose that we have the following code:

#include <iostream>

struct Foo
{
    Foo() { std::cout << "Foo::Foo()" << std::endl; }
    Foo(Foo&) { std::cout << "Foo::Foo(Foo&)" << std::endl; }
    Foo(Foo&&) { std::cout << "Foo::Foo(Foo&&)" << std::endl; }
    ~Foo() { std::cout << "Foo::~Foo()" << std::endl; }
};

struct Bar
{
    Foo foo;
};

int main()
{
    Bar instance{ Foo{} };
}

What should any implementation do here according to the Standard? Does it require both copy and move constructor to exist (not to be deleted)? Which one should it call?

FrozenHeart
  • 19,844
  • 33
  • 126
  • 242
  • Possible duplicate of [What are copy elision and return value optimization?](https://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization) –  Sep 12 '17 at 06:43

1 Answers1

3

Since Foo{} is a prvalue and you preform copy initialization, the answer depends on what standard revision you are working with.

C++14 requires the move c'tor to be accessible and not deleted. Even though the actual move may and most probably will be elided.

C++17 will directly initialize the member foo, so both copy and move c'tor need not exist or be accessible.


As an aside, if you opt out of copy initialization and do direct initialization, you can delete both the move and copy c'tors in C++14 as well:

#include <iostream>

struct Foo
{
    Foo() { std::cout << "Foo::Foo()" << std::endl; }
    Foo(Foo&) = delete;
    Foo(Foo&&) = delete;
    ~Foo() { std::cout << "Foo::~Foo()" << std::endl; }
};

struct Bar
{
    Foo foo;
};

int main()
{
    Bar instance{ {} };
}
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 1
    In C++14, don't you only need one of copy or move, not move specifically? – Daniel H Sep 12 '17 at 06:48
  • What about the copy constrictor? Can it be deleted then? – FrozenHeart Sep 12 '17 at 06:48
  • 1
    @DanielH - You need the one that will be picked in overload resolution. That's the move c'tor. – StoryTeller - Unslander Monica Sep 12 '17 at 06:49
  • @FrozenHeart - Yes, it can. – StoryTeller - Unslander Monica Sep 12 '17 at 06:52
  • @StoryTeller It [looks like](https://godbolt.org/g/jB71KM), at least with GCC, you can avoid having a move constructor as long as you just don't declare it, instead of explicitly deleting it. The compiler doesn't generate one, and if you pass `-fno-elide-constructors` it calls the copy constructor. If you uncomment the move constructor deletion it fails to compile. – Daniel H Sep 12 '17 at 07:07
  • @DanielH - The move c'tor is considered ODR-used even if the move is elided. So this is an ODR violation, no diagnostic required. It's "ok" but still against the specification as written. – StoryTeller - Unslander Monica Sep 12 '17 at 07:09
  • @StoryTeller According to [this page](http://en.cppreference.com/w/cpp/language/move_constructor) an implicitly deleted move constructor doesn’t participate in overload resolution, specifically to allow copy construction here. I don’t know where that is in the standard, but this seems more likely, because it changes the meaning of valid code. In C++03, given `class A { A(); A(A const& other); };` the code `A a = A();` would be valid. This class has no move constructor in C++11+, and it isn’t rare, so they wouldn’t change the semantics. – Daniel H Sep 12 '17 at 15:42
  • @DanielH - That has nothing to do with the odr-ude of an explicitly deleted c'tor, or a declared but ubdefined one. – StoryTeller - Unslander Monica Sep 12 '17 at 16:31
  • @StoryTeller I thought you were talking about the version I linked, where the move constructor was only mentioned in a comment and wasn’t being implicitly defined. If you explicitly delete it, or declare it but don’t define it, then you have use of an explicitly deleted function or an undefined symbol, both of which the compiler does error out on. – Daniel H Sep 12 '17 at 16:33
  • @DanielH - I misunderstood your previous comment. For some reason I read "not declare" as "not define". What I referred to was [code like this](http://coliru.stacked-crooked.com/a/3dd820b3a75c0522). This one has on ODR violation, but gets away with it because there is no diagnostic required (which the compiler abuses to elide the move and carry on). – StoryTeller - Unslander Monica Sep 12 '17 at 16:40
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/154250/discussion-between-daniel-h-and-storyteller). – Daniel H Sep 12 '17 at 16:46