0
votes

I have a class like this, except with multiple members:

struct S {
    S( S&& s ) : member( std::move( s.member ) ) {}
    std::vector< int > member;
};

I basically want aggregate initialization on it like so:

S s{ {} };

But that seems to be impossible due to the custom constructor, so I'd like to emulate it using constructor arguments:

struct S {
    S( S&& s ) : member( std::move( s.member ) ) {}
    S( std::vector< int >&& vec ) : member( std::move( vec ) ) {}
    S( const std::vector< int >& vec ) : member( vec ) {}
    std::vector< int > member;
};

That looks like a lot of redundancy just to move if possible and copy otherwise, especially with more members since the number of permutations explodes. I think I want perfect forwarding?

struct S {
    template< typename V >
    S( V&& vec ) : member( std::forward< V >( vec ) ) {}
    S( S&& s ) : member( std::move( s.member ) ) {}
    std::vector< int > member;
};

But now I can't call it with an empty initializer list like so:

S s( {} );

Because the initializer list argument type can't be deduced. Is there any solution that doesn't require me to write

S s( std::vector<int>{} );

?

1
What would the difference be between e.g. S s{ {} }; and S s;? I.e. between your wanted constructor and the default constructor?Some programmer dude
Why do you need a custom move constructor, does it do anything the implicit one wouldn't? Get rid of the user-provided constructor and you have an aggregate.Jonathan Wakely
@Joachim In my actual code I have additional parameters and it's just the one that's occasionally empty.Mr. Wonko

1 Answers

5
votes

It's not clear why adding a default constructor isn't an option for you, allowing simply S s{}:

struct S {
    S() = default;
    S( S&& s ) = default;
    std::vector< int > member;
};

If that isn't suitable you have a number of options. The most obvious is to get rid of your move constructor, so the class is an aggregate. The move constructor you've shown is pointless anyway, it doesn't do anything the implicitly-defined one wouldn't do.

struct S {
    std::vector< int > member;
};

The next option is to add an initializer-list constructor:

struct S {
    S() = default;
    S( S&& s ) = default;
    S(std::initializer_list<int> il) : member(il) { }
    std::vector< int > member;
};

That will allow you to pass a braced-init-list to the vector member.

Another option, if you only want to be able to pass an empty initializer list to the vector, is to overload the constructor so that passing something to the vector is optional (but you can still pass arguments for the other members). That means you simply omit the pointless {} argument for the vector and let it be default-constructed.