The ? operator and temporary objects ...

H

Henryk

Hello,

what is the scope of an object that is created within one expresseion
of the "?" operator.

Let me try to give an example

#inlcude <string>

std::string somefunc() {
return "Some Text";
}

int main(int argc, char *argv[]) {
// does p1 contain a valid address?
const char *p1 = true ? ::somefunc().c_str() : NULL;

// working alternative?
std::string s = true ? ::somefunc() : "";
const char p2 = s.c_str();

return 0;
}

My concern: somefunc() returns a temporary string object. c_str()
returns a valid const char pointer to the content of that temporary
object. But does the temporary string object still exist outside the
"?" operator line? If not, then pointer p1 points to freed memory.

Thanks for your help
 
J

Johannes Schaub (litb)

Henryk said:
Hello,

what is the scope of an object that is created within one expresseion
of the "?" operator.

Let me try to give an example

#inlcude <string>

std::string somefunc() {
return "Some Text";
}

int main(int argc, char *argv[]) {
// does p1 contain a valid address?
const char *p1 = true ? ::somefunc().c_str() : NULL;

// working alternative?
std::string s = true ? ::somefunc() : "";
const char p2 = s.c_str();

return 0;
}

My concern: somefunc() returns a temporary string object. c_str()
returns a valid const char pointer to the content of that temporary
object. But does the temporary string object still exist outside the
"?" operator line? If not, then pointer p1 points to freed memory.

It exists outside that line in case the expression statement spans multiple
lines

const char *p1 = (true ? ::somefunc().c_str() : NULL)
+ 0; // still lives here

But it won't live when processing the next full expression.The means if
instead of true ? .. : "" you would have written false ? .. : "p1", you
would have done undefined behavior.

The temporary object has no scope (because it has no name). It has lifetime.
Its lifetime starts when it is constructed, and ends after the full
expression it appears in was completely evaluated, except for the temporary
(if any) that holds the result of the evaluation.

In

char const *p1 = string("hello").c_str();

Behavior is undefined, because the implementation could destruct the string
before p1 is initialized (the string temporary does not represent the result
of the initalization), which means you would write a pointer value into
another pointer that is already invalid. However if "string" had an implicit
conversion to "const char*", the following would be fine, I think.

char const *p1 = string("hello");

In both cases however, the string temporary will have been destructed after
"p1" was initialized (and sometimes earlier). And therefor, in the next
full-expression to be evaluated, "p1" is an invalid pointer, and reading it
will invoke undefined behavior too.

A further complication is made when the type of "p1" is a class type. In
this case, an implicit constructor function call is done to initialize it,
and the implicit call of that function is considered an expression (see
1.9/12 C++03).

ClassObj p1 = string("hello").c_str();

In this case, the string temporary is guaranteed to be kept alive until the
initialization of "p1". That's a good thing, since the constructor of "p1"
probably wants to rely on it and could actually do something very useful
with that C string.

Your "working alternative" definitely works.

See 12.2/4 in C++03 for the elaborated explanation. I find it is difficult
standardese, so I think some stuff I talked about above could actually be
plain wrong :)
 
H

Henryk

Hello Johannes,

thank you for you reply.

Your in-depth explanation basically affirms, what were my "feelings".




But that leads me to another question. I often see something like this
in programs i'm working on:

std::eek:stringstream ss;

// usually convert some value
ss << "Some Text";

// call some existing c style function, e.g. printf
printf("%s", ss.str().c_str());

I assume that the temporary string object returned by ss.str() is not
guaranteed to be still alive when the result of c_str() is finally
used in the function printf.

As a workaround I need to assign the str() result to some local
variable to assure expected lifetime.

std::eek:stringstream ss;

ss << "Some Text";

{
std::string s = ss.str();
::printf("%s", s.c_str());
}

Am I right? :eek:)
 
J

Johannes Schaub (litb)

Henryk said:
Hello Johannes,

thank you for you reply.

Your in-depth explanation basically affirms, what were my "feelings".




But that leads me to another question. I often see something like this
in programs i'm working on:

std::eek:stringstream ss;

// usually convert some value
ss << "Some Text";

// call some existing c style function, e.g. printf
printf("%s", ss.str().c_str());

I assume that the temporary string object returned by ss.str() is not
guaranteed to be still alive when the result of c_str() is finally
used in the function printf.

No, it is still alive. It is alive until the end evaluating the complete
full-expression. The printf call is part of that full-expression. So this is
all-fine.

It is a problem in C, which specifies that the return value of a function
would only be valid until the next sequence point, which basically meant you
couldn't pass addresses of array members of structs to functions (like
passing a char array member to printf).

C1x, next C's revision, fixes that by introducing full-expression
temporaries, IIRC.

As a workaround I need to assign the str() result to some local
variable to assure expected lifetime.

std::eek:stringstream ss;

ss << "Some Text";

{
std::string s = ss.str();
::printf("%s", s.c_str());
}

Am I right? :eek:)

This works, but is not needed :)
 
H

Henryk

Alright, I now see the difference.

The following would not work, because the pointer is not valid at the
time it is used.

const char *p = ss.str().c_str();
::printf("%s", p);


But this works for the reasons you explained:

::printf("%s", ss.str().c_str());

Thank you for that insight!
 

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,770
Messages
2,569,583
Members
45,072
Latest member
trafficcone

Latest Threads

Top