I'm trying to write a (C++98) program in which the following pattern occurs: I have a very simple generic tuple class, and I need to fill it with values constructed using a factory. Minimal example code is below:
#include <iostream>
class diagnostics
{
private:
int payload;
public:
diagnostics(int a)
: payload(a)
{
std::cout << "constructor\n";
}
diagnostics(const diagnostics& o)
: payload(o.payload)
{
std::cout << "copy constructor\n";
}
~diagnostics()
{
std::cout << "destructor [" << payload << "]\n";
}
};
struct creator
{
static diagnostics create()
{
static int i = 0;
return diagnostics(++i);
}
};
template<class Head, class Tail>
struct tuple
{
Head head;
Tail tail;
typedef Head head_t;
typedef Tail tail_t;
tuple(const Head& h, const Tail& t)
: head(h), tail(t)
{
}
};
struct empty_tuple { };
template<class Tuple, class Create>
struct create_helper
{
static Tuple create()
{
return Tuple(Create::create(),
create_helper<typename Tuple::tail_t, Create>::create());
}
};
template<class Create>
struct create_helper<empty_tuple, Create>
{
static empty_tuple create()
{
return empty_tuple();
}
};
template<class Tuple, class Create>
Tuple create()
{
//return Tuple(Create::create(), empty_tuple()); //(*)
return create_helper<Tuple, Create>::create();
}
int main()
{
typedef tuple<diagnostics, empty_tuple> tuple_t;
tuple_t a = create<tuple_t, creator>();
}
The output is
constructor
copy constructor
destructor [1]
destructor [1]
I would like to get rid of the middle two lines.
For simplifying the discussion, we can in the above code uncomment the line marked (*); this will break the genericisity but not the program.
Now to my main question: How can I fix this situation? Is something in the standard preventing RVO (presumably RVO would have to be done recursively here)? If not, accepting that the compiler is just not good enough, is there a way I can cause this to happen in an explicit way? I'm ok with complicating the callside of create(), but I don't want to complicate the objects in the tuple (in particular, some of them cannot be default constructed, and I don't want to introduce additional "unitialized" states to them). Could emplace new maybe help?
The following question seems related, but was ultimately not helpful: Why does not RVO happen here?)
I feel a bit stupid to answer my own question after five minutes, but this does seems to be the most appropriate way to get the following information across:
Here is one solution. We can transfer the creation code into the tuple class itself:
A compatible implementation of create is
I'd still be interested in other methods.