1

I have an object which is non-copyable and which requires an argument to its contructor:

class Foo
{
  public:
    Foo() = delete;
    Foo(const Foo &) = delete;
    Foo(int x);

  private:
    int member;
};

Foo::Foo(int x) : member(x)
{
}

I have another class which contains as a member an array of such objects:

class Bar
{
  public:
    Bar(int a);

  private:
    Foo members[4];
};

Suppose that my object Foo is really far too big and its constructor far too complicated for a temporary or duplicate copy to ever exist or the constructor to ever be called more than once for each item of the array.

How do I write the constructor for the containing class to pass the arguments to the items in the member array?

I have tried:

Bar::Bar(int a) : members { a, a+1, a+2, a+3 }
{
}

g++ says "use of deleted function Foo::Foo(const Foo&)".

[Edit] I have also tried:

Bar::Bar(int a) : members { {a}, {a+1}, {a+2}, {a+3} }
{
}

as suggested by Yksisarvinen. This also says "use of deleted function Foo::Foo(const Foo&)", but only if I declare a destructor for Foo. Without a destructor this compiles correctly. How do I get it to compile for a class which has a destructor?

Tom V
  • 4,827
  • 2
  • 5
  • 22

1 Answers1

4

Three options here, depending on what you actually want to do:

1. Use C++17

C++17 has guaranteed copy elision, which will solve the problem.

2. Provide a move constructor

Compiler chooses copy constructor, because by declaring your own copy constructor you prevented possibility to generate a move constructor. Depending on your actual class, move constructor may or may not be viable.

Foo(Foo &&) = default; //or implement "stealing" of the resource here

Note that by the rule of five you should also provide copy/move assignment operators and destructor (defaulted or not).

3. Wrap each array element in braces

If neither of above options is valid, the workaround is to simply wrap each of the array elements in its own set of braces

Bar::Bar(int a) : members { {a}, {a+1}, {a+2}, {a+3} }

In general, your previous option relied on conversion from int to Foo. Once the compiler does the conversion, it has to copy (or move, but you excluded that) the converted object to array, and that triggers an error. By wrapping them in braces, the elements are initialized as Foo objects (and not as int to be converted to Foo), and no move/copy is needed.

Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52
  • Hi, thanks for the reply. The option 3 compiles, but only as long as the destructor of Foo is implicit. As soon as I add a a declaration of `~Foo();` inside the class (with or without a definition) it starts complaining about use of the deleted copy constructor again. – Tom V Feb 01 '21 at 14:27