Problem: I have a non-copyable object with two constructors. I need to create an object with one of the constructors and then use it within some common code:-
With a copyable object it would look like this, and be easy:
Object a;
if (condition)
a = Object(p1);
else
a = Object(p2,p3,p4);
a.doSomething();
But, the object is non-copyable, so I've had to do this:
boost::scoped_ptr<Object> a;
if (condition)
a = new Object(p1);
else
a = new Object(p2,p3,p4);
a->doSomething();
This feels too complex. Is there a better solutiuon?
Here's a very terrible hack, assuming Object is default-constructible:
Object a;
a.~Object();
if (condition) { ::new (&a) Object(p1); }
else { ::new (&a) Object(p2, p3, p4); }
Don't use this.
Another option is using a union, but you'll need to invoke the destructor manually in that setup as well.
A cleaner solution could be achieved with Boost.Optional (using in-place factories). (Thanks to #K-Ballo for digging up the details!)
#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
struct Object
{
explicit Object(int) {}
explicit Object(int, float, std::string) {}
Object(Object const &) = delete;
Object(Object &&) = delete;
Object & operator=(Object const &) = delete;
Object & operator=(Object &&) = delete;
};
boost::optional<Object> a;
if (condition) { a = boost::in_place(0); }
else { a = boost::in_place(0, 1.0f, "two" ); }
Looks perfectly reasonable to me just the way it is. It's clear, simple and relatively concise.
auto const doSomethingTo = []( Object&& o ) { o.doSomething(); };
doSomethingTo( condition? Object( p1 ) : Object( p1, p2, p3 ) );
Disclaimer: code not touched by compiler.
EDIT: the code above, when the Object( Object&& ) constructor is private, fails to compile with MSVC 11.0 (yes even last year's November CTP), but does compile fine with MinGW g++ 4.7.1 and with some version of clang.
It appears that it should compile.
So, it's probably a bug in Visual C++ – but unfortunately I didn't find an easy workaround.
An uneasy workaround for the assumed-to-be Visual C++ compiler bug:
#include <fstream>
#include <iostream>
using namespace std;
class Object
{
private:
Object( Object const& );
Object( Object&& );
public:
void doSomething() const {}
Object( int ) {}
Object( int, int, int ) {}
};
int main( int argc, char* argv[] )
{
int p1 = 0, p2 = 0, p3 = 0;
bool condition = argc == 2;
auto const doSomething1 = [=]() { Object o( p1 ); o.doSomething(); };
auto const doSomething2 = [=]() { Object o( p1, p2, p3 ); o.doSomething(); };
if( condition ) { doSomething1(); } else { doSomething2(); }
}
Another answer maintains that new (read: a dynamic allocation) is your only option.
That's wrong.
There's really nothing wrong with your solution, although as
others have already mentionned, it would be more readably if you
used the conditional operator rather than if's. But you should
consider the possibility of refactoring. If you factor out all
of the code which uses the object into a separate function
(taking the object by reference), then something like:
if ( condition ) {
Object a( p1 );
doWhatever( a );
} else {
Object a( p2, p3, p4 );
doWhatever( a );
}
might be preferable (or not—I don't think that there's any
"right" answer with regards to choosing between these two).
I don't see the complexity... If you need to construct efficiently based on an if-condition declaring a pointer and using new is your only option. What you don't necessarily need to do is:
Use a scoped_ptr (altough that's usually a good idea)
Have the constructor in the if in your "main" code. Yours is a typical use case for a factory (see e.g. http://en.wikipedia.org/wiki/Factory_method_pattern).
EDIT: added "efficiently" in the second sentence.
I think your code is OK.
You just may want to consider the conditional operator ? :
boost::scoped_ptr<Object> a( condition ? new Object(p1) : new Object(p2,p3,p4) );
a->doSomething();
So, here is a quick trick to make this work, without manually constructing objects. Instead, I created a Deferred<T> template that represents an object in automatic storage whose construction is deferred (and possibly never occurs).
The buff in Deferred should be replaced with a union, because that will handle alignment issues (assuming you have C++11 features to support that). Asserts that constructed is true when you call get() is probably a good idea.
Once you have constructed your object, you can implicitly cast your Deferred<T> to a T&, then use that T& as an alias to the deferred-constructed T.
In theory, you could do away with the constructed bool if you could prove that it would always be constructed, but I'd advise against it. Other than that, this should be nearly as efficient as you can pull it off. And with the C++11 union case, it might even be standards compliant.
Oh yes, and it should be enhanced with perfect forwarding.
#include <utility>
// does not handle alignment issues:
template<typename T>
struct Deferred {
Deferred():constructed(false) {}
operator T&() { return get(); }
T& get() { return *reinterpret_cast<T*>(&buff[0]); }
template<typename... Args>
T& construct( Args... args ) {
new(&buff[0]) T(args...);
constructed = true;
return get();
}
~Deferred() {
if (constructed) {
get().~T();
}
}
private:
bool constructed;
char buff[sizeof(T)];
};
#include <iostream>
struct Object {
bool is_int;
Object( int x ):is_int(true) {}
Object( double d ):is_int(false) {}
~Object() {
std::cout << "~Object("<<is_int<<") destroyed\n";
}
};
enum which_test {
as_int,
as_double,
do_not,
};
void test(which_test v) {
std::cout << v << "\n";
Deferred<Object> o;
if(v==as_int) {
o.construct( 7 );
} else if (v==as_double) {
o.construct( 7.0 );
} else {
}
}
int main() {
test(as_int);
test(as_double);
test(do_not);
}
Related
C++11 gaves us the ability to create anonymous unions with non-trivial members. That can be very useful sometimes - for example, if I want to create Holder class for some non-trivial object without default ctor.
Let's make this NonTrivial object more interesting by giving it a virtual method:
#include <stdint.h>
#include <stdio.h>
struct Base
{
virtual void something() { printf("something\n"); }
};
struct NonTrivial : Base
{
explicit NonTrivial( int ) : a(1), b(2), c(3), d(4) { printf("NonTrivial\n"); }
virtual void something() override { printf("something non trivial\n"); }
int a;
int b;
int c;
int d;
};
struct Holder
{
Holder() : isNonTrivial(false), dummy(0x77) {}
Holder( NonTrivial n) : isNonTrivial(true), nonTrivial( n ) {}
bool isNonTrivial;
union
{
int dummy;
NonTrivial nonTrivial;
};
Holder & operator=( const Holder & rhs )
{
isNonTrivial = rhs.isNonTrivial;
if( isNonTrivial )
nonTrivial = rhs.nonTrivial;
return *this;
}
};
int main() {
Holder holder_1;
NonTrivial n(1);
Holder holder_2( n );
holder_1 = holder_2;
holder_2.nonTrivial.something();
holder_1.nonTrivial.something();
return 0;
}
This just works. However, this works only because compiler doesn't actually make a virtual call here. Let's force it:
Base * ptr = &holder_1.nonTrivial;
ptr->something();
This produces a segfault.
But why? I did more or less the obvious thing - checked if holder holds a non-trivial object and if so - copied it.
After reading the assembly I saw that this operator= doesn't actually copy vtable pointer from rhs.nonTrivial. I assume that this happens because operator= for NonTrivial should only be called on fully-constructed object and fully-constructed object should already have its vtable pointer initialized - so why bother and copy it?
Questions:
Is my thinking correct?
How should operator= look like to
create a full copy of nonTrivial object? I have two ideas - delete
operator= entirely and force the user to use copy ctor - or use
placement new instead of nonTrivial = rhs.nonTrivial - but maybe
there are some other options?
P.S. I'm aware of std::optional and the like, I'm trying to understand how to do it myself.
If anybody will stumble upon this question looking for a quick answer, here's how I solved this issue using placement new:
template< typename T, typename ... Args >
void inplace_new( T & obj, Args && ... args )
{
auto * t = &obj;
t = new(t) T{ args... };
}
Holder & operator=( const Holder & rhs )
{
isNonTrivial = rhs.isNonTrivial;
if( isNonTrivial )
inplace_new( nonTrivial, rhs.nonTrivial );
return *this;
}
Don't forget #include <new> :)
Here
https://youtu.be/dTeKf5Oek2c?t=2939
Stephen T. Lavavej says in a talk that const auto && is not useful.
Isn't the following a valid use case?
NonMovableNonCopyable create () {
// ...
return {};
}
const auto && obj = create ();
Concrete application: In XLL code, MS Excel generally does not like its xlopers to be copied or moved which it returns, since after copying or moving it will not be able to correctly free them.
Instead of
const auto && obj = create ();
... write just
const auto object = create();
... or
const auto object{ create() };
The = relies on the compiler eliding the copy constructor call, but I don't know about any extant compiler that fails to do that.
The clarity gained is IMO much more important than the guarantee offered by the reference (and if using reference, do use an ordinary & reference). Namely, one avoids having a maintenance programmer wasting time on trying to understand the rationale for the reference. If the type was explicitly specified it could be a case of polymorphic reference, Petru Marginean's trick, but with auto that's out of the question, and so the maintenance programmers are left scratching their heads, for some period of paid time.
On the other hand, const T&& can be useful for function overloading as an argument type to catch the case of a temporary as argument, in the same way as && qualifier for member function was considered sufficiently useful to be adopted in the standard. For example, even though I do not recommend this, if a pointer to the actual argument is stored for later use, then presumably one doesn't want to store a pointer to a temporary, which will end up as a dangling pointer:
struct Expr{ virtual ~Expr(){} };
struct Sum_expr: Expr
{
const Expr* a_;
const Expr* b_;
Sum_expr( Expr const& a,Expr const& b ): a_( &a ), b_( &b ) {}
template< class A >
Sum_expr( A&&, Expr const&& b ) = delete;
template< class B >
Sum_expr( Expr const&& a, B&& ) = delete;
};
auto main()
-> int
{
Expr a;
Expr b;
Sum_expr sum{ a, b };
Sum_expr sum2{ a, Expr() }; //! Nope, won't compile.
}
Note: Here A&& and B&& support both rvalue and lvalue actual arguments, i.e. they're not necessarily rvalue references, because they're universal references.
But instead of overloading and differentiating the cases I think I'd make that formal argument a pointer, even if a pointer technically can be a nullpointer, because as I see it, with what I'm accustomed to, it communicates the intent more clearly.
Sum_expr( Expr const* a, Expr const* b );
Const T && is very useful if T has a mutable field. A common example is a bool m_movedFrom that's initialized to false and gets set to true when moved from. This allows the rest of your object - resource handle, for example - to remain const.
class Resource // use this as a base class of const correct classes
{
private:
mutable bool m_movedFrom;
protected:
Resource()
: m_movedFrom(false)
{
}
Resource(const Resource&& other)
: m_movedFrom(false)
{
other.m_movedFrom = true;
}
bool isOwning() const // call from descendant's destructor
{
return m_movedFrom;
}
};
EDIT: a more complicated example explaining when the object is itself const, but a global state is not (not claiming this is the good way to solve this, it's for illustrational purposes only):
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
typedef std::string object_type;
typedef std::string seek_type;
class GoalSeeker
{
private:
static std::vector<const GoalSeeker*> s_store;
const std::vector<const GoalSeeker*>::iterator m_iter;
const object_type m_data;
public:
GoalSeeker( const object_type& data )
: m_iter( s_store.insert( s_store.end(), this ) ), m_data(data)
{
}
GoalSeeker( const GoalSeeker& ) = delete;
GoalSeeker( const GoalSeeker&& other )
: m_iter( other.m_iter ), m_data( other.m_data )
{
*m_iter = this;
}
~GoalSeeker()
{
if( *m_iter == this )
{
// cleanup m_data
}
}
static bool seek( const seek_type& needle )
{
return std::find_if(s_store.begin(), s_store.end(),
[&needle](const GoalSeeker* haystack)
{
return haystack->m_data.find(needle) != std::string::npos;
}) != s_store.end();
}
};
std::vector<const GoalSeeker*> GoalSeeker::s_store = {};
GoalSeeker fn()
{
return GoalSeeker("def");
}
int main() {
GoalSeeker a( "abc" );
GoalSeeker b( "cde" );
GoalSeeker s( (const GoalSeeker&&) fn() );
std::cout << GoalSeeker::seek( "de" ) << " " << GoalSeeker::seek( "fff" );
}
I know that RVO is mostly applied but can I count on it? I have a function that creates an object of class FlagContainer.
class FlagContainer {
public:
~FlagContainer() {
someItem->flag = true;
}
private:
Item * someItem;
}
public FlagContainer createFlagContainer() {
return FlagContainer();
}
After the caller used the container, the flag must be set. So I can do this with the destructor.
{
FlagContainer container = createFlagContainer();
// do something with container
}
When out of scope, the destructor will be called. But can I be sure that the destructor will never be called in createFlagContainer? Is there any way to achieve this?
I would use AVR GCC 4.7.0 compiler.
I know that RVO is mostly applied but can I count on it?
Don't rely on RVO for logic. Put simply, someone compiling your program can switch it off with a command-line option.
Is there any way to achieve this?
Surprisingly, the standard library already gives you this functionality so you don't need to run the risk of implementing it yourself (move constructors and operators are notoriously difficult to get right)
std::unique_ptr with a custom deleter does the job nicely.
#include <iostream>
#include <memory>
#include <cassert>
// test type
struct X
{
bool flag = false;
};
// a custom deleter that sets a flag on the target
struct flag_setter_impl
{
template<class X>
void operator()(X* px) const {
if (px) {
assert(!px->flag);
std::cout << "setting flag!" << std::endl;
px->flag = true;
}
}
};
// a type of unique_ptr which does not delete, but sets a flag
template<class X>
using flag_setter = std::unique_ptr<X, flag_setter_impl>;
// make a flag_stter for x
template<class X>
auto make_flag_setter(X& x) -> flag_setter<X>
{
return flag_setter<X>(&x, flag_setter_impl());
}
// quick test
auto main() -> int
{
using namespace std;
X x;
{
auto fs1 = make_flag_setter(x);
auto fs2 = move(fs1);
}
return 0;
}
but I don't have the STL on my target
Then don't forget your rules of 0, 3, 5
#include <iostream>
#include <memory>
#include <cassert>
// test type
struct X
{
bool flag = false;
};
// a custom deleter that sets a flag on the target
struct flag_setter_impl
{
template<class X>
void operator()(X* px) const {
if (px) {
assert(!px->flag);
std::cout << "setting flag!" << std::endl;
px->flag = true;
}
}
};
// a type of unique_ptr which does not delete, but sets a flag
template<class X>
struct flag_setter
{
flag_setter(X* px) : px(px) {}
flag_setter(const flag_setter&) = delete;
flag_setter(flag_setter&& r) noexcept : px(r.px) { r.px = nullptr; }
flag_setter& operator=(const flag_setter& r) = delete;
flag_setter& operator=(flag_setter&& r)
{
flag_setter tmp(std::move(r));
std::swap(tmp.px, px);
return *this;
}
~flag_setter() noexcept {
flag_setter_impl()(px);
}
private:
X* px;
};
// make a flag_stter for x
template<class X>
auto make_flag_setter(X& x) -> flag_setter<X>
{
return flag_setter<X>(&x);
}
// quick test
auto main() -> int
{
using namespace std;
X x;
{
auto fs1 = make_flag_setter(x);
auto fs2 = move(fs1);
}
return 0;
}
There is no guarantee [yet] that copy-elision is applied. Guaranteed copy-elision is proposed for inclusion into C++17. Whether copy-elision is applied is entirely at the discretion of the compiler (some compilers have an option to entirely disable it, though).
A potential approach avoiding this need might be the use of an essentially unusable type which can be used only as the constructor argument for the type you are interested in being used and to return an object of that type:
class FlagContainerBuilder {
friend class FlagContainer;
public:
FlagContainerBuilder(/* suitable arguments go here */);
// nothing goes here
};
class FlagContainer {
// ...
public:
FlagContainer(FlagContainerBuilder&& builder);
// as before
};
FlagContainerBuilder createFlagContainer() { ... }
This way you avoid the need to potentially destroy a FlagContainer returned from createFlagContainer().
I know that RVO is mostly applied but can I count on it?
No. Compilers are allowed to implement RVO, but not required. You can only count on it, when your compiler promises to do so.
Although this particular case as per standard 12.8/3/p31.1 Copying and moving class objects [class.copy] renders as a context that the compiler can do NRVO (aka copy elision), you can't rely on it. A program that relies on this kind of optimization is effectively non portable.
To ensure move of the object I would define a move constructor and inside I would null the pointer of the other object, while in the destructor I would check whether the pointer is nullptr in order to set its flag true:
class FlagContainer {
public:
FlagContainer(FlagContainer&& other) : someItem(other.someItem) {
other.someItem = nullptr;
}
~FlagContainer() {
if(someItem) someItem->flag = true;
}
Item * someItem;
};
FlagContainer createFlagContainer() {
return FlagContainer();
}
Live Demo
I have a function in a class that I want the compiler to use NRVO on...all the time...even in debug mode. Is there a pragma for this?
Here is my class that works great in "release" mode:
template <int _cbStack> class CBuffer {
public:
CBuffer(int cb) : m_p(0) {
m_p = (cb > _cbStack) ? (char*)malloc(cb) : m_pBuf;
}
template <typename T> operator T () const {
return static_cast<T>(m_p);
}
~CBuffer() {
if (m_p && m_p != m_pBuf)
free(m_p);
}
private:
char *m_p, m_pBuf[_cbStack];
};
The class is used to make a buffer on the stack unless more than _cbStack bytes are required. Then when it destructs, it frees memory if it allocated any. It's handy when interfacing to c functions that require a string buffer, and you are not sure of the maximum size.
Anyway, I was trying to write a function that could return CBuffer, like in this test:
#include "stdafx.h"
#include <malloc.h>
#include <string.h>
template <int _cbStack> CBuffer<_cbStack> foo()
{
// return a Buf populated with something...
unsigned long cch = 500;
CBuffer<_cbStack> Buf(cch + 1);
memset(Buf, 'a', cch);
((char*)Buf)[cch] = 0;
return Buf;
}
int _tmain(int argc, _TCHAR* argv[])
{
auto Buf = foo<256>();
return 0;
}
I was counting on NRVO to make foo() fast. In release mode, it works great. In debug mode, it obviously fails, because there is no copy constructor in my class. I don't want a copy constructor, since CBuffer will be used by developers who like to copy everything 50 times. (Rant: these guys were using a dynamic array class to create a buffer of 20 chars to pass to WideCharToMultiByte(), because they seem to have forgotten that you can just allocate an array of chars on the stack. I don't know if they even know what the stack is...)
I don't really want to code up the copy constructor just so the code works in debug mode! It gets huge and complicated:
template <int _cbStack>
class CBuffer {
public:
CBuffer(int cb) : m_p(0) { Allocate(cb); }
CBuffer(CBuffer<_cbStack> &r) {
int cb = (r.m_p == r.m_pBuf) ? _cbStack : ((int*)r.m_p)[-1];
Allocate(cb);
memcpy(m_p, r.m_p, cb);
}
CBuffer(CBuffer<_cbStack> &&r) {
if (r.m_p == r.m_pBuf) {
m_p = m_pBuf;
memcpy(m_p, r.m_p, _cbStack);
} else {
m_p = r.m_p;
r.m_p = NULL;
}
}
template <typename T> operator T () const {
return static_cast<T>(m_p);
}
~CBuffer() {
if (m_p && m_p != m_pBuf)
free((int*)m_p - 1);
}
protected:
void Allocate(int cb) {
if (cb > _cbStack) {
m_p = (char*)malloc(cb + sizeof(int));
*(int*)m_p = cb;
m_p += sizeof(int);
} else {
m_p = m_pBuf;
}
}
char *m_p, m_pBuf[_cbStack];
};
This pragma does not work:
#pragma optimize("gf", on)
Any ideas?
It is not hard to make your code both standards conforming and work.
First, wrap arrays of T with optional extra padding. Now you know the layout.
For ownership use a unique ptr instead of a raw one. If it is vapid, operator T* returns it, otherwise buffer. Now your default move ctor works, as does NRVO if the move fails.
If you want to support non POD types, a bit of work will let you both suppoort ctors and dtors and move of array elements and padding bit for bit.
The result will be a class that does not behave surprisingly and will not create bugs the first time someome tries to copy or move it - well not the first, that would be easy. The code as written will blow up in different ways at different times!
Obey the rule of three.
Here is an explicit example (now that I'm off my phone):
template <size_t T, size_t bufSize=sizeof(T)>
struct CBuffer {
typedef T value_type;
CBuffer();
explicit CBuffer(size_t count=1, size_t extra=0) {
reset(count, extra);
}
void resize(size_t count, size_t extra=0) {
size_t amount = sizeof(value_type)*count + extra;
if (amount > bufSize) {
m_heapBuffer.reset( new char[amount] );
} else {
m_heapBuffer.reset();
}
}
explicit operator value_type const* () const {
return get();
}
explicit operator value_type* () {
return get();
}
T* get() {
return reinterpret_cast<value_type*>(getPtr())
}
T const* get() const {
return reinterpret_cast<value_type const*>(getPtr())
}
private:
std::unique_ptr< char[] > m_heapBuffer;
char m_Buffer[bufSize];
char const* getPtr() const {
if (m_heapBuffer)
return m_heapBuffer.get();
return &m_Buffer[0];
}
char* getPtr() {
if (m_heapBuffer)
return m_heapBuffer.get();
return &m_Buffer[0];
}
};
The above CBuffer supports move construction and move assignment, but not copy construction or copy assignment. This means you can return a local instance of these from a function. RVO may occur, but if it doesn't the above code is still safe and legal (assuming T is POD).
Before putting it into production myself, I would add some T must be POD asserts to the above, or handle non-POD T.
As an example of use:
#include <iostream>
size_t fill_buff(size_t len, char* buff) {
char const* src = "This is a string";
size_t needed = strlen(src)+1;
if (len < needed)
return needed;
strcpy( buff, src );
return needed;
}
void test1() {
size_t amt = fill_buff(0,0);
CBuffer<char, 100> strBuf(amt);
fill_buff( amt, strBuf.get() );
std::cout << strBuf.get() << "\n";
}
And, for the (hopefully) NRVO'd case:
template<size_t n>
CBuffer<char, n> test2() {
CBuffer<char, n> strBuf;
size_t amt = fill_buff(0,0);
strBuf.resize(amt);
fill_buff( amt, strBuf.get() );
return strBuf;
}
which, if NRVO occurs (as it should), won't need a move -- and if NRVO doesn't occur, the implicit move that occurs is logically equivalent to not doing the move.
The point is that NRVO isn't relied upon to have well defined behavior. However, NRVO is almost certainly going to occur, and when it does occur it does something logically equivalent to doing the move-constructor option.
I didn't have to write such a move-constructor, because unique_ptr is move-constructable, as are arrays inside structs. Also note that copy-construction is blocked, because unique_ptr cannot be copy-constructed: this aligns with your needs.
In debug, it is quite possibly true that you'll end up doing a move-construct. But there shouldn't be any harm in that.
I don't think there is a publicly available fine-grained compiler option that only triggers NRVO.
However, you can still manipulate compiler optimization flags per each source file via either changing options in Project settings, command line, and #pragma.
http://msdn.microsoft.com/en-us/library/chh3fb0k(v=vs.110).aspx
Try to give /O1 or /O2 to the file that you want.
And, the debug mode in Visual C++ is nothing but a configuration with no optimizations and generating debugging information (PDB, program database file).
If you are using Visual C++ 2010 or later, you can use move semantics to achieve an equivalent result. See How to: Write a Move Constructor.
I have a class that does a transformation on a string, like so
class transer{
transer * parent;
protected:
virtual string inner(const string & s) = 0;
public:
string trans(const string & s) {
if (parent)
return parent->trans(inner(s));
else
return inner(s);
}
transer(transer * p) : parent(p) {}
template <class T>
T create() { return T(this); }
template <class T, class A1> // no variadic templates for me
T create(A1 && a1) { return T(this, std::forward(a1)); }
};
So I can create a subclass
class add_count : public transer{
int count;
add_count& operator=(const add_count &);
protected:
virtual string inner(const string & s) {
return std::to_string((long long)count++) + s;
}
public:
add_count(transer * p = 0) : transer(p), count(0) {}
};
And then I can use the transformations:
void use_transformation(transer & t){
t.trans("string1");
t.trans("string2");
}
void use_transformation(transer && t){
use_trasnformation(t);
}
use_transformation(add_count().create<add_count>());
Is there a better design for this? I'd like to avoid using dynamic allocation/shared_ptr if I can, but I'm not sure if the temporaries will stay alive throughout the call. I also want to be able to have each transer be able to talk to its parent during destruction, so the temporaries also need to be destroyed in the right order. It's also difficult to create a chained transformation and save it for later, since
sometrans t = add_count().create<trans1>().create<trans2>().create<trans3>();
would save pointers to temporaries that no longer exist. Doing something like
trans1 t1;
trans2 t2(&t1);
trans3 t3(&t2);
would be safe, but annoying. Is there a better way to do these kinds of chained operations?
Temporaries will be destructed at the end of the full expression, in the
reverse order they were constructed. Be careful about the latter,
however, since there are no guarantees with regards to the order of
evaluation. (Except, of course, that of direct dependencies: if you
need one temporary in order to create the next—and if I've
understood correctly, that's your case—then you're safe.)
If you don't want dynamic allocation you either pass the data which is operated on to the function that initiates the chain, or you need a root type which holds it for you ( unless you want excessive copying ). Example ( might not compile ):
struct fooRef;
struct foo
{
fooRef create() { return fooRef( m_Val ); }
foo& operator=( const fooRef& a_Other );
std::string m_Val;
}
struct fooRef
{
fooRef( std::string& a_Val ) : m_Val( a_Val ) {}
fooRef create() { return fooRef( m_Val ); }
std::string& m_Val;
}
foo& foo::operator=( const fooRef& a_Other ) { m_Val = a_Other.m_Val; }
foo startChain()
{
return foo();
}
foo expr = startChain().create().create(); // etc
First the string lies on the temporary foo created from startChain(), all the chained operations operates on that source data. The assignment then at last copies the value over to the named var. You can probably almost guarantee RVO on startChain().