I would like to have a struct initialize its member with forwarding arguments. This compiles and works fine except when I declare a destructor and when I try to return the struct from a function (which I believe necessitates a copy constructor).
#include <utility>
struct Foo
{
int val;
Foo(int val) : val(val)
{
}
};
struct FooContainer
{
Foo member;
template<typename... Args>
FooContainer(Args&&... args) :
member(std::forward<Args>(args)...)
{}
~FooContainer() {}
};
FooContainer getFooContainer()
{
FooContainer retval(0);
return retval;
}
int main() {}
The compiler error is:
example.cc: In constructor ‘FooContainer::FooContainer(Args&& ...) [with Args = FooContainer&]’:
example.cc:27: instantiated from here
example.cc:18: error: no matching function for call to ‘Foo::Foo(FooContainer&)’
example.cc:7: note: candidates are: Foo::Foo(int)
example.cc:4: note: Foo::Foo(const Foo&)
It looks like it's trying to generate a copy constructor for FooContainer
but fails because it doesn't have a way to initialize Foo. Yet if I remove the FooContainer
constructor or destructor, it compiles fine.* Why does it do this?
*on http://cpp.sh/ with GCC 4.9.2 anyway. g++ 4.4.3 on Ubuntu gives the same error even if the destructor isn't declared.
I can't tell you exactly why this happens (a standard-expert will be able to) but the problem is actually caused because you have defined a user-defined destructor.
Remove that and the problem goes away (you want to use the rule-of-zero anyway, right?)
If you must have the destructor and can't refactor it away for some reason, then replacing the move-constructor (which you implicitly deleted by providing a destructor) will also solve it.
Solution 1 - use rule of 0:
#include <utility>
struct Foo
{
int val;
Foo(int val) : val(val)
{
}
};
struct FooContainer
{
Foo member;
template<typename... Args>
FooContainer(Args&&... args) :
member(std::forward<Args>(args)...)
{}
// ~FooContainer() {}
};
FooContainer getFooContainer()
{
FooContainer retval(0);
return retval;
}
int main() {}
Solution 2 - use rule of 5:
#include <utility>
struct Foo
{
int val;
Foo(int val) : val(val)
{
}
};
struct FooContainer
{
Foo member;
template<typename... Args>
FooContainer(Args&&... args) :
member(std::forward<Args>(args)...)
{}
FooContainer(const FooContainer&) = default;
FooContainer(FooContainer&&) = default;
FooContainer& operator=(const FooContainer&) = default;
FooContainer& operator=(FooContainer&&) = default;
~FooContainer() {}
};
FooContainer getFooContainer()
{
FooContainer retval(0);
return retval;
}
int main() {}