problem with std::forward_as_tuple in MIL

F

Frank Bergemann

Hi,
i have problems to create and forward tuple in MIL (member
initialization list).
Here's the program:
------------------------------- snip
--------------------------------------------------
#include <iostream>
#include <tuple>

template <typename T>
struct Elem
{
const T & _data;
Elem(const T & inp)
:_data(inp)
{ };

Elem(const Elem &)
{
std::cout << "!!! Elem copy c'tor invoked !!!" << std::endl;
};
};

template <typename T>
std::eek:stream &
operator<<(
std::eek:stream & out,
const Elem<T> & arg)
{
out << "Elem<T>(" << arg._data << ")";
return out;
}

/* Host class to actually hold tuple<> */
template <typename ...Types>
struct HostClassImpl
{
typedef typename std::tuple<Elem<Types>&&...> Type;

const Type _data;

HostClassImpl(Type && data)
: _data(data)
{ };

HostClassImpl(const HostClassImpl &)
{
std::cout << "HostClassImpl copy c'tor!" << std::endl;
};

HostClassImpl & operator=(const HostClassImpl &)
{
std::cout << "HostClassImpl assignment operator!" << std::endl;
return *this;
};
};

/*
* Host class front-end supporting variadic arguments
* just to enable the user level to do without std::forward_as_tuple()
* So HostClass actually shall do the std::forward_as_tuple() for the
user.
*/
template <typename ...Types>
struct HostClass
{
HostClassImpl<Types...> _impl;

template <typename ...Args>
HostClass(
Args&& ... args)
: _impl(std::forward_as_tuple(args...))
{ };

HostClass(const HostClass &)
{
std::cout << "HostClass copy c'tor!" << std::endl;
};

HostClass & operator=(const HostClass &)
{
std::cout << "HostClass assignment operator!" << std::endl;
return *this;
};
};

struct DummyHostClass
{
HostClassImpl<int, int, int> _impl;

DummyHostClass(void)
: _impl(std::forward_as_tuple(Elem<int>(10), Elem<int>(11),
Elem<int>(12)))
{
log();
};

void log(void)
{
std::cout << "DummyHostClass log data:" << std::endl;
std::cout << std::get<0>(_impl._data) << std::endl;
std::cout << std::get<1>(_impl._data) << std::endl;
std::cout << std::get<2>(_impl._data) << std::endl;
}

~DummyHostClass()
{
std::cout << "DummyHostClass d'tor" << std::endl;
};
};

int
main(
int argc,
char ** argv)
{

std::cout << std::endl << "Test (1): use Elem<int> only:" <<
std::endl;
Elem<int> elem(5);
std::cout << elem << std::endl;

std::cout << std::endl << "Test (2): create tuple<Elem<int>&&> - one
element only:" << std::endl;
std::tuple<Elem<int>&&> tuple(std::forward_as_tuple(Elem<int>(3)));
std::cout << std::get<0>(tuple) << std::endl;

std::cout << std::endl << "Test (3): create tuple<Elem<int>&&>,
Elem<int>&&> - two elements:" << std::endl;
std::tuple<Elem<int>&&, Elem<int>&&>
tuple2(std::forward_as_tuple(Elem<int>(5), Elem<int>(7)));
std::cout << std::get<0>(tuple2) << std::endl;
std::cout << std::get<1>(tuple2) << std::endl;

std::cout << std::endl << "Test (4): use HostClassImpl::Type
defnition for tuple<Elem<int>&&, Elem<int>&&> - two elements:" <<
std::endl;
HostClassImpl<int,int>::Type
tuple3(std::forward_as_tuple(Elem<int>(8), Elem<int>(9)));
std::cout << std::get<0>(tuple3) << std::endl;
std::cout << std::get<1>(tuple3) << std::endl;

std::cout << std::endl << "Test (5): use HostClassImpl c'tor passing
already constructed tuple:" << std::endl;
HostClassImpl<int, int> hostImpl(std::forward_as_tuple(Elem<int>(10),
Elem<int>(11)));
std::cout << std::get<0>(hostImpl._data) << std::endl;
std::cout << std::get<1>(hostImpl._data) << std::endl;

std::cout << std::endl << "Test (6): use DummyHostClass with fixed
HostClassImpl within:" << std::endl;
DummyHostClass dummy;
dummy.log();

#if 0
std::cout << std::endl << "Test (7): use HostClass c'tor passing
individual tuple element (variadic template):" << std::endl;
HostClass<int, int> host(Elem<int>(12), Elem<int>(13));
std::cout << std::get<0>(host._impl._data) << std::endl;
std::cout << std::get<1>(host._impl._data) << std::endl;
#endif

return 0;
}
------------------------------- snap
--------------------------------------------------

Here's the output:

------------------------------- snip
--------------------------------------------------

Test (1): use Elem<int> only:
Elem<T>(5)

Test (2): create tuple<Elem<int>&&> - one element only:
Elem<T>(3)

Test (3): create tuple<Elem<int>&&>, Elem<int>&&> - two elements:
Elem<T>(5)
Elem<T>(7)

Test (4): use HostClassImpl::Type defnition for tuple<Elem<int>&&,
Elem<int>&&> - two elements:
Elem<T>(8)
Elem<T>(9)

Test (5): use HostClassImpl c'tor passing already constructed tuple:
Elem<T>(10)
Elem<T>(11)

Test (6): use DummyHostClass with fixed HostClassImpl within:
DummyHostClass log data:
Elem<T>(10)
Elem<T>(11)
Elem<T>(12)
DummyHostClass log data:
Elem<T>(10)
Elem<T>(11871724)
Elem<T>(-1079275032)
DummyHostClass d'tor

------------------------------- snap
--------------------------------------------------

Why it the data away after leaving the constructor of DummyHostClass?
(Btw: 'DummyHostClass' is just a step towards intented 'HostClass' to
localize the problem.)

- many thanks!

best regards,
Frank
 
S

SG

[...]
Why it the data away after leaving the constructor of DummyHostClass?
(Btw: 'DummyHostClass' is just a step towards intented 'HostClass' to
localize the problem.)

I just skimmed Elem<> and HostClassImpl<> and stopped right there.
What is the point of all this? What are you actually trying to achieve
here? What is the point of Elem? It just wraps a reference. What is
the point of HostClassImpl? It just wraps a tuple of rvalue references
to Elem objects which itself wrap lvalue references. Surely this is
not what you want. That much I can tell you...

The first couple of lines of main already invoke undefined behaviour:

1 cout << "\nTest (1): use Elem<int> only:" << endl;
2 Elem<int> elem(5);
3 cout << elem << endl;

During execution of line 2 a temporary int object will be created and
initialized with 5 and it will be destroyed immeadately when execution
reaches the semicolon. Since Elem is defined like this:

template <typename T>
struct Elem
{
const T & _data;
Elem(const T & inp)
:_data(inp) {}
};

the object called 'elem' will store a dangling reference to an int-
object that does not exist anymore beyond line 2. Accessing this non-
existing int-object in line 3 invokes undefined behaviour.

You should avoid the use of references, *especially* rvalue
references, if you don't know what you are doing.

Cheers!
SG
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top