Is this kosher?

T

Tim H

class Foo {};

template <typename Tdata>
const Foo &operator<<(const Foo &foo, const Tdata &data)
{
std::cout << "FOO: " << data << std::endl;
}


Foo
MyFunction1()
{
// returning a copy - can this ever trigger Foo::Foo() and
Foo::~Foo() ?
return Foo() << "MyFucntion1";
}

const Foo &
MyFunction2()
{
// chaining calls on a temporary?
// is the temporary's life extended by returning a reference?
return MyFunction1() << "MyFunction2" << "something" << "else";
}


Tim
 
L

Leon Timmermans

// returning a copy - can this ever trigger Foo::Foo() and Foo::~Foo
() ?

What do you mean with 'triggering Foo::Foo()'?
// chaining calls on a temporary?
// is the temporary's life extended by returning a reference? return
return MyFunction1() << "MyFunction2" << "something" << "else";

The temporary is destroyed at the end of the statement. Returning a
reference to a variable made on the stack is going to make your program
crash.

Regards,

Leon Timmermans
 
A

AnonMail2005

class Foo {};

template <typename Tdata>
const Foo &operator<<(const Foo &foo, const Tdata &data)
{
    std::cout << "FOO: " << data << std::endl;

}

Foo
MyFunction1()
{
    // returning a copy - can this ever trigger Foo::Foo() and
Foo::~Foo() ?
    return Foo() << "MyFucntion1";

}

const Foo &
MyFunction2()
{
    // chaining calls on a temporary?
    // is the temporary's life extended by returning a reference?
    return MyFunction1() << "MyFunction2" << "something" << "else";

}

Tim

I think I see what you're trying to do.

Make operator << a member function of your foo object.
Remove the first argument (it will be implicit for a member function)
and have it return *this.

Then, both your functions should work fine. Of course, MyFunction1
will return a copy of your object.

HTH
 
T

Tim H

I should have asked this better, I was in a rush. Sorry

class Foo {};

template <typename Tdata>
const Foo &operator<<(const Foo &foo, const Tdata &data)
{
std::cout << "FOO: " << data << std::endl;

}

This code works:
Foo() << "string";

This code seems to work:
Foo() << "string" << 99 << "other" << "more";

Is the temporary Foo guaranteed to exist until the end of this full
statement, or can the temporary possibly go away somewhere in the
middle?

This code works, and returns a valid Foo.
Foo MyFunction1()
{
return Foo();
}

Is this guaranteed to only construct one Foo? Or can the compiler
optionally construct a local Foo and then copy construct the returned
Foo in caller's scope?


This code works, and returns a valid Foo.
Foo MyFunction2()
{
return Foo() << "MyFunction1";
}

As I understand, it returns a copy of the temporary Foo, right?
Meaning that the Foo constructor will be called twice (for two
seperate instances).


How about this?
const Foo &MyFunction3()
{
return MyFunction1();
}

Or this?
const Foo &MyFunction4()
{
return MyFunction1() << "something" << "else";
}

Both of those are returning references to temporaries, right? Yet the
compiler did not complain. Both of these are illegal, as I
understand. The temporary values do not have their lifetime extended,
am I correct?

Thanks

Tim
 
G

gpderetta

Depends on the caller. If it is bound to a const reference, no copy ctor
calls occur.

This is not guaranteed. A compiler is still free to make a copy (most
recent compilers don't, but that's another story).
 
T

Tim H

This is not guaranteed. A compiler is still free to make a copy (most
recent compilers don't, but that's another story).

What if the use case is
MyFunction1() << "some" << "stuff";

Does the compiler have the option to make a copy (e.g. can the Foo
ctor/dtor be called more than once) ??

Tim
 
J

James Kanze

I should have asked this better, I was in a rush. Sorry
This code works:
Foo() << "string";
This code seems to work:
Foo() << "string" << 99 << "other" << "more";
Is the temporary Foo guaranteed to exist until the end of this
full statement, or can the temporary possibly go away
somewhere in the middle?

The temporary is guaranteed to last until the end of the full
expression, in this case, until the end of the statement.
This code works, and returns a valid Foo.
Foo MyFunction1()
{
return Foo();
}
Is this guaranteed to only construct one Foo? Or can the
compiler optionally construct a local Foo and then copy
construct the returned Foo in caller's scope?

The formal semantics are for it to construct a temporary Foo,
then copy construct it for the return. The compiler is
explicitly allowed to elide the copy, however, even if it has
side effects. Whoever uses the return value is likely to have
to copy construct it as well, formally; again, in certain
contexts, the compiler is allowed to elide the copy.

Note that even if the compiler does elide the copy, the copy
must be legal.
This code works, and returns a valid Foo.
Foo MyFunction2()
{
return Foo() << "MyFunction1";
}
As I understand, it returns a copy of the temporary Foo,
right?

Almost certainly, since all it has to return is a reference, and
not an object (with known lifetime, etc.).
Meaning that the Foo constructor will be called twice
(for two seperate instances).

Two different constructors will be called; the default
constructor and the copy constructor.
How about this?
const Foo &MyFunction3()
{
return MyFunction1();
}

That's a good recepe for undefined behavior. You're returning a
reference to a temporary object, which will be destructed before
the return.
Or this?
const Foo &MyFunction4()
{
return MyFunction1() << "something" << "else";
}
Both of those are returning references to temporaries, right?
Right.

Yet the compiler did not complain. Both of these are illegal,
as I understand. The temporary values do not have their
lifetime extended, am I correct?

Correct. It's undefined behavior, so the compiler is not
required to complain. In the second case, supposing separate
compilation and a non-inlined operator<<, it's difficult to see
how it could detect the error to begin with.

Are you trying to do something specific, or are these just
general questions? Some of your code looks very much like my
logging objects; the solution here will be an rvalue reference,
once the next version of the standard is adopted and
implemented. Until then, it can be simulated using reference
counting, implementing copy so that all copied instances are
idempotent, and destruction so that only the destructor of the
last copied instance does whatever is critical.
 
J

James Kanze


[...]
Depends on the caller. If it is bound to a const reference, no
copy ctor calls occur.

That's wrong. The formal syntax is to create a temporary
instance using the default constructor, then copy it (using a
copy constructor) to where ever the compiler puts return values.
The compiler may elide the copy (and most do), but it's not
required, and it is required that the copy constructor exist and
is accessible, even if the copy doesn't take place.
Copy ctor is a different thing than a ctor (although
pronounced the same ;-)

A copy constructor is just another constructor. The only
particularity it has is that if you don't declare one, the
compiler will. Other than that, however, the fact that you are
"copying" is irrelevant; overload resolution chooses which
constructor will be called, exactly like in any other context
(and there are a few exotic cases where the constructor which is
called in the above is NOT the copy constructor, but some other
constructor).
It will be called here probably, yes, but can be optimized
away as well, I guess (copy ctor can always be optimized away
AFAIK).

Not always, but most of the time. (In this case, I don't think
it can legally be optimized away, except under the as if rule.
But I'm not 100% sure.)

[...]
Yes, the lifetime extension by assigning to a const reference
only works on the caller side.

There is never a lifetime extension due to assignment. The
lifetime of a temporary will be extended if the temporary is
used to initialize a reference (which must be const), but only
if the temporary is the initialization expression. It has
nothing to do with caller side or otherwise. (There's also a
special rule which says that this extension doesn't apply when
returning a reference.)
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top