Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

inlining of functions returning an unwindable object

11 views
Skip to first unread message

Martin B.

unread,
Nov 24, 2009, 11:13:09 AM11/24/09
to
Hi all.

( Note that I've posted this to microsoft.public.vc.language a few days
ago, but didn't really get an answer there, so I hope someone may shed
some light on this in a more general context. )

The Visual Studio compiler will never inline a funtion that returns an
unwindable object (e.g. std::string, CString, etc.)
This is also true for a simple getter function that only contains one
return statement.

Does anyone know why this is and what other compilers do in such a case?
(I tried to find out for GCC, but didn't find any docs.)

While the details below are for VC, I think the question is really
interesting for all C++ developers.

See details below.

cheers,
Martin

-------- Original Message --------
Subject: inlining of functions returning an unwindable object -- rationale?
Date: Thu, 19 Nov 2009 10:01:11 +0100
From: Martin B. <0xCDC...@gmx.at>
Newsgroups: microsoft.public.vc.language

Greetings.

The Visual Studio compiler will never inline a funtion that returns an
unwindable object (e.g. std::string, CString, etc.)

(See documentation of the inline and __forceinline keyword and the doc
for C4714:
http://msdn.microsoft.com/en-us/library/a98sb923.aspx ,
http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx
)

Can anyone provide a rationale for this? It seems quite weird to me.

Consider this example:
class Simple {
public:
std::string s_;

public:
Simple()
: s_("test")
{ }

std::string get() {
return s_;
}
...

void testsimple()
{
Simple oSimple;
std::string s1( oSimple.get() );
...

void testsimple2()
{
Simple oSimple;
std::string s1( oSimple.s_ );
...

This will always, no matter what, generate a call to get(). (If you
specifiy __forceinline and activate C4714 you'll get that warning)

Find the assembly below, of which I do not claim to understand much, but
it certainly doesn't seem to me as if there's any reason for this.
Especially consider the case where the member is accessed directly. The
calls to the string related functions are exactly the same!
That is, both version will call string functions in this order:
1) string::string (ctor of Simple)
2) string::string (ctor of s1)
3) string::~string
4) string::~string

So what's the deal with not inlining such a simple getter function ??

Find the assembly (VS 2005 / VC8) below.

cheers,
Martin


***************

==>
void testsimple()
{
00401150 push ebp
00401151 mov ebp,esp
00401153 push 0FFFFFFFFh
00401155 push offset __ehhandler$?testsimple@@YAXXZ (402211h)
0040115A mov eax,dword ptr fs:[00000000h]
00401160 push eax
00401161 sub esp,3Ch
00401164 mov eax,dword ptr [___security_cookie (405004h)]
00401169 xor eax,ebp
0040116B mov dword ptr [ebp-10h],eax
0040116E push eax
0040116F lea eax,[ebp-0Ch]
00401172 mov dword ptr fs:[00000000h],eax
Simple oSimple;
00401178 push offset string "test" (403194h)
0040117D lea ecx,[ebp-2Ch]
00401180 call dword ptr
[__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char>
>::basic_string<char,std::char_traits<char>,std::allocator<char> >
(403094h)]
00401186 mov dword ptr [ebp-4],0
std::string s1( oSimple.get() );
0040118D lea eax,[ebp-48h]
00401190 push eax
00401191 lea ecx,[ebp-2Ch]
00401194 call Foo::get (401380h)
}
00401199 lea ecx,[ebp-48h]
0040119C call dword ptr
[__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char>
>::~basic_string<char,std::char_traits<char>,std::allocator<char> >
(40308Ch)]
004011A2 mov dword ptr [ebp-4],0FFFFFFFFh
004011A9 lea ecx,[ebp-2Ch]
004011AC call dword ptr
[__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char>
>::~basic_string<char,std::char_traits<char>,std::allocator<char> >
(40308Ch)]
004011B2 mov ecx,dword ptr [ebp-0Ch]
004011B5 mov dword ptr fs:[0],ecx
004011BC pop ecx
004011BD mov ecx,dword ptr [ebp-10h]
004011C0 xor ecx,ebp
004011C2 call __security_check_cookie (4019D6h)
004011C7 mov esp,ebp
004011C9 pop ebp
004011CA ret

==>
std::string get() {
00401380 push ebp
00401381 mov ebp,esp
00401383 sub esp,8
00401386 mov dword ptr [ebp-8],ecx
00401389 mov dword ptr [ebp-4],0
return s_;
00401390 mov eax,dword ptr [this]
00401393 push eax
00401394 mov ecx,dword ptr [ebp+8]
00401397 call dword ptr
[__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char>
>::basic_string<char,std::char_traits<char>,std::allocator<char> >
(403090h)]
0040139D mov ecx,dword ptr [ebp-4]
004013A0 or ecx,1
004013A3 mov dword ptr [ebp-4],ecx
004013A6 mov eax,dword ptr [ebp+8]
}
004013A9 mov esp,ebp
004013AB pop ebp
004013AC ret 4


**************************

==>
void testsimple2()
{
004011D0 push ebp
004011D1 mov ebp,esp
004011D3 push 0FFFFFFFFh
004011D5 push offset __ehhandler$?testsimple2@@YAXXZ (4022CEh)
004011DA mov eax,dword ptr fs:[00000000h]
004011E0 push eax
004011E1 sub esp,3Ch
004011E4 mov eax,dword ptr [___security_cookie (405004h)]
004011E9 xor eax,ebp
004011EB mov dword ptr [ebp-10h],eax
004011EE push eax
004011EF lea eax,[ebp-0Ch]
004011F2 mov dword ptr fs:[00000000h],eax
Simple oSimple;
004011F8 push offset string "test" (403194h)
004011FD lea ecx,[ebp-2Ch]
00401200 call dword ptr
[__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char>
>::basic_string<char,std::char_traits<char>,std::allocator<char> >
(403094h)]
00401206 mov dword ptr [ebp-4],0
std:string s1( oSimple.s_ );
0040120D lea eax,[ebp-2Ch]
00401210 push eax
00401211 lea ecx,[ebp-48h]
00401214 call dword ptr
[__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char>
>::basic_string<char,std::char_traits<char>,std::allocator<char> >
(403090h)]
}
0040121A lea ecx,[ebp-48h]
0040121D call dword ptr
[__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char>
>::~basic_string<char,std::char_traits<char>,std::allocator<char> >
(40308Ch)]
00401223 mov dword ptr [ebp-4],0FFFFFFFFh
0040122A lea ecx,[ebp-2Ch]
0040122D call dword ptr
[__imp_std::basic_string<char,std::char_traits<char>,std::allocator<char>
>::~basic_string<char,std::char_traits<char>,std::allocator<char> >
(40308Ch)]
00401233 mov ecx,dword ptr [ebp-0Ch]
00401236 mov dword ptr fs:[0],ecx
0040123D pop ecx
0040123E mov ecx,dword ptr [ebp-10h]
00401241 xor ecx,ebp
00401243 call __security_check_cookie (401A66h)
00401248 mov esp,ebp
0040124A pop ebp
0040124B ret

************************

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

George Neuner

unread,
Nov 24, 2009, 3:40:52 PM11/24/09
to
On Tue, 24 Nov 2009 10:13:09 CST, "Martin B." <0xCDC...@gmx.at>
wrote:

>Hi all.
>
>( Note that I've posted this to microsoft.public.vc.language a few days
>ago, but didn't really get an answer there, so I hope someone may shed
>some light on this in a more general context. )
>
>The Visual Studio compiler will never inline a funtion that returns an
>unwindable object (e.g. std::string, CString, etc.)

Documented.

>This is also true for a simple getter function that only contains one
>return statement.

No it isn't. See below.


>Does anyone know why this is ...

The first problem is, your code is not C++ but "managed C++", which
has its own rules - note the references to ___security_cookie() and
__security_check_cookie() in your disassembly, those won't appear in
regular C++ code. VC++ won't inline any function that has a CLR
security descriptor because the security check could throw an
exception. If you want to have the best approximation of C++, make
sure your project is "ATL", "MFC" or "Win32" - "CLR" projects produce
managed C++.


As for non-throwing C++, if you change the type of s_ and add some use
of the results so all the functions aren't optimized completely away
as in the following:

class MYPOINT
{
public:
int x;
int y;
public:
MYPOINT( int i, int j ) { x = i; y = j; };
MYPOINT operator=( MYPOINT src ) { return MYPOINT(src.x,src.y);
};
};

class Simple
{
public:
MYPOINT s_;

public:
Simple()
: s_(3,4)
{ };

MYPOINT get() { return s_; };
};


void testsimple()
{
Simple oSimple;
MYPOINT s1( oSimple.get() );
cout << s1.x;
}


int main()
{
testsimple();
}


... you'll see from the disassembly below that, in testsimple(), the
calls to the constructor and to get() have been completely elided and
the constant value of the constructor argument has been passed through
directly to the stream write call. This was compiled with VC++08 in
release mode with /O2/Ob1/Oi/Ot. Note that even though "inline
declarations only (/Ob1)" was specified, the compiler ended up
inlining the constructor and get() anyway ... likely as the result of
constant propagation and dead code removal.


***************
PUBLIC ?testsimple@@YAXXZ ; testsimple
; Function compile flags: /Ogtpy
; COMDAT ?testsimple@@YAXXZ
_TEXT SEGMENT
?testsimple@@YAXXZ PROC ; testsimple, COMDAT

; 47 : {

00000 51 push ecx

; 48 : Simple oSimple;
; 49 : MYPOINT s1( oSimple.get() );
; 50 : cout << s1.x;

00001 6a 03 push 3
00003 e8 00 00 00 00 call
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z ;
std::basic_ostream<char,std::char_traits<char> >::operator<<
00008 59 pop ecx

; 51 : }

00009 c3 ret 0
?testsimple@@YAXXZ ENDP ; testsimple

***************


>... and what other compilers do in such a case?


>(I tried to find out for GCC, but didn't find any docs.)

You'd have to try them.

George

George Neuner

unread,
Nov 25, 2009, 9:16:08 AM11/25/09
to
On Tue, 24 Nov 2009 14:40:52 CST, George Neuner <gneu...@comcast.net>
wrote:

> MYPOINT operator=( MYPOINT src ) { return MYPOINT(src.x,src.y);

Ignore this. I had started to do something more complicated and then
realized I didn't need to ... I just quickly dashed off something that
compiled forgot about it.

Martin B.

unread,
Nov 25, 2009, 9:19:39 AM11/25/09
to
George Neuner wrote:
> On Tue, 24 Nov 2009 10:13:09 CST, "Martin B." <0xCDC...@gmx.at>
> wrote:
>> (...)

>> The Visual Studio compiler will never inline a funtion that returns an
>> unwindable object (e.g. std::string, CString, etc.)
>
> Documented.
>
>> This is also true for a simple getter function that only contains one
>> return statement.
>
> No it isn't. See below.
>

Yes it is. See below.

>
>> Does anyone know why this is ...
>
> The first problem is, your code is not C++ but "managed C++", which
> has its own rules - note the references to ___security_cookie() and
> __security_check_cookie() in your disassembly, those won't appear in

> regular C++ code. (...) If you want to have the best approximation of C++, make


> sure your project is "ATL", "MFC" or "Win32" - "CLR" projects produce
> managed C++.
>

Incorrect. The security_cookie is generated by the /GS option (see:
http://msdn.microsoft.com/en-us/library/8dbf701c%28VS.80%29.aspx) adding
/GS- to the commandline will remove these references from the disassembly.

FWIW, here are my settings (unchanged from the OP except for /GS-):
General -
Config. Type: Application (.exe)
Character Set: Unicode
CLR support: *No* CLR support
C++ Optimization - (I have only enabled inlining so I do not have to
care about the compiler optimizing away unused code)
Opt: Custom
Inlining: /Ob2
Whole Program Opt.: No
C++ Language -
Disable Language Extensions: *Yes* (/Za)
Enable RTTI: Yes

The commandline looks like this:
/Ob2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /FD
/EHsc /MD /Za /Yu"stdafx.h" /Fp"Release\inline_opt.pch" /Fo"Release\\"
/Fd"Release\vc80.pdb" /W3 /nologo /c /Zi /TP /errorReport:prompt
plus: /w34714 /GS-

>
> As for non-throwing C++, if you change the type of s_ and add some use
> of the results so all the functions aren't optimized completely away
> as in the following:
>
> class MYPOINT
> {
> public:
> int x;
> int y;
> public:
> MYPOINT( int i, int j ) { x = i; y = j; };
> MYPOINT operator=( MYPOINT src ) { return MYPOINT(src.x,src.y);

> (...)

I have checked and your example does *not* contain an *unwindable*
object. MYPOINT has an empty dtor, because it contains only int members.
Adding a non-empty dtor to mypoint, for example:
MYPOINT::~MYPOINT() {
cout << "This is a non-empty dtor\n";
}
will effectively prevent MYPOINT get() from being inlined.


And so I repeat my question to the general audience: Why?? :-)

cheers,
Martin

Goran

unread,
Nov 25, 2009, 9:24:32 AM11/25/09
to
On Nov 24, 9:40 pm, George Neuner <gneun...@comcast.net> wrote:
> On Tue, 24 Nov 2009 10:13:09 CST, "Martin B." <0xCDCDC...@gmx.at>

> wrote:
>
> >Hi all.
>
> >( Note that I've posted this to microsoft.public.vc.language a few days
> >ago, but didn't really get an answer there, so I hope someone may shed
> >some light on this in a more general context. )
>
> >The Visual Studio compiler will never inline a funtion that returns an
> >unwindable object (e.g. std::string, CString, etc.)
>
> Documented.
>
> >This is also true for a simple getter function that only contains one
> >return statement.
>
> No it isn't. See below.
>
> >Does anyone know why this is ...
>
> The first problem is, your code is not C++ but "managed C++", which
> has its own rules - note the references to ___security_cookie() and
> __security_check_cookie() in your disassembly, those won't appear in
> regular C++ code. VC++ won't inline any function that has a CLR
> security descriptor because the security check could throw an
> exception. If you want to have the best approximation of C++, make
> sure your project is "ATL", "MFC" or "Win32" - "CLR" projects produce
> managed C++.

AFAIK, "security cookie" code is the result of /GS option and that is
available for native compilation. Note also that Martin's example only
uses std::string, so no CLR is needed there, and I'd be very surprised
if he had that in his options.

That said, could this have something to do with compiler not knowing
the implementation of ~string, hence forcing a function call (not that
I see a connection)? As you've shown, when destructor is visible to
the compiler, all is indeed inline (but as usual with such examples,
one has to make sure that some unseen optimization doesn't play with
"expected" results).

Goran.

Martin B.

unread,
Nov 25, 2009, 12:53:17 PM11/25/09
to
Goran wrote:
> On Nov 24, 9:40 pm, George Neuner <gneun...@comcast.net> wrote:
>> On Tue, 24 Nov 2009 10:13:09 CST, "Martin B." <0xCDCDC...@gmx.at>
>> wrote:
>>
>>> Hi all.
>>> ( Note that I've posted this to microsoft.public.vc.language a few days
>>> ago, but didn't really get an answer there, so I hope someone may shed
>>> some light on this in a more general context. )
>>> The Visual Studio compiler will never inline a funtion that returns an
>>> unwindable object (e.g. std::string, CString, etc.)
> (...)

> That said, could this have something to do with compiler not knowing
> the implementation of ~string, hence forcing a function call (not that
> I see a connection)? As you've shown, when destructor is visible to
> the compiler, all is indeed inline (but as usual with such examples,

As I have asserted in my other reply, it does not matter if the dtor is
visible. What was relevant in George's example was that MYPOINT did not
have a dtor. If you add a dtor that does anything, get() won't be inlined.

> one has to make sure that some unseen optimization doesn't play with
> "expected" results).
>

Yes. These examples can easily be compiled with only the /Ob2 or /Ob1
switch and all other optimizations disabled, which is what I'm doing.

cheers,
Martin

George Neuner

unread,
Nov 27, 2009, 8:29:25 AM11/27/09
to
On Wed, 25 Nov 2009 08:19:39 CST, "Martin B." <0xCDC...@gmx.at>
wrote:

>George Neuner wrote:
>> On Tue, 24 Nov 2009 10:13:09 CST, "Martin B." <0xCDC...@gmx.at>
>> wrote:
>

>> The first problem is, your code is not C++ but "managed C++", ...


>
>Incorrect. The security_cookie is generated by the /GS option (see:
>http://msdn.microsoft.com/en-us/library/8dbf701c%28VS.80%29.aspx) adding
>/GS- to the commandline will remove these references from the disassembly.

My bad. I blanked on buffer check because I've been doing a lot of
managed code recently and got used to seeing checks in disassemblies.


>I have checked and your example does *not* contain an *unwindable*
>object. MYPOINT has an empty dtor, because it contains only int members.
>Adding a non-empty dtor to mypoint, for example:
> MYPOINT::~MYPOINT() {
> cout << "This is a non-empty dtor\n";
> }
>will effectively prevent MYPOINT get() from being inlined.

Miscommunication I think. I generally associate "unwind(able)" with
use of exceptions, not just having a dtor. Since string and stream (as
above) operations can throw and it's documented that throw code won't
be inlined, I created code that didn't.

Anyway, you are right that having a non-trivial dtor makes VC++ call
the value object returning function rather than inlining it. Of
course the obvious fix is (if possible) to return a reference to the
object instead.

I changed the code as follows to have a non-trivial dtor:

static int constructs = 0;
static int destructs = 0;

class MYPOINT
{
public:
int x, y, z;
public:
MYPOINT( int i, int j, int k )
{
x = i; y = j; z = k;
++constructs;
};

MYPOINT( MYPOINT& src )
{
x = src.x; y = src.y; z = src.z;
++constructs;
}

~MYPOINT()
{
++destructs;
}
};

class Simple
{
public:
MYPOINT s_;

public:
Simple()
: s_(3,4,7)
{ }

MYPOINT get()
{
return s_;
}
};

void testsimple()
{
Simple oSimple;
MYPOINT s1( oSimple.get() );

cout << s1.z << endl;
}


VC++ compiles the explicit call to get() as you expected. However,
GCC4 (MinGW) refuses to compile this code:

test.cpp: In function 'void testsimple()':
test.cpp:61: error: no matching function for call to
'MYPOINT::MYPOINT(MYPOINT)'

and so does VC++ if you disable MS's language extensions:

Error 1 error C2558: class 'MYPOINT' : no copy constructor
available or copy constructor is declared 'explicit'
test.cpp 61 test

If you change get() to return MYPOINT&, then both compilers will
inline the get() call in testsimple() regardless of the dtor.


As for why the nontrivial dtor prevents inlining functions that value
return objects, I can only speculate ... I haven't had occasion to
look under the hood of a C++ compiler in quite a few years.

My best guess is that it's too much trouble - in general - for the
compiler to decide whether the temporary return object can be renamed
and used directly or will need to be destructed in the caller - so
there is a blanket edict against inlining such functions.

The experts in comp.compilers are likely to know more (or at least
guess better).

George

Martin B.

unread,
Nov 28, 2009, 3:39:57 AM11/28/09
to
George Neuner wrote:
> On Wed, 25 Nov 2009 08:19:39 CST, "Martin B." <0xCDC...@gmx.at>
> wrote:
>> I have checked and your example does *not* contain an *unwindable*
>> object. MYPOINT has an empty dtor, because it contains only int members.
>> Adding a non-empty dtor to mypoint, for example:
>> MYPOINT::~MYPOINT() {
>> cout << "This is a non-empty dtor\n";
>> }
>> will effectively prevent MYPOINT get() from being inlined.
>
> Miscommunication I think. I generally associate "unwind(able)" with
> use of exceptions, not just having a dtor. Since string and stream (as
> above) operations can throw and it's documented that throw code won't
> be inlined, I created code that didn't.
>

It has nothing to do with throw but with catch.
A function such as
inline int get_throwing_x() const {
if(x > 0)
return x;
else
throw std::runtime_error("x is not > 0!");
}
will be inlined allright.

What will be *not* inlined is a function containing a try:
inline int get_try_x() const {
try {
s.empty();
return x-1;
} catch(std::exception const& e) {
return x+strlen(e.what());
}
}
... will not be inlined. (see:
http://msdn.microsoft.com/en-us/library/a98sb923.aspx)

> Anyway, you are right that having a non-trivial dtor makes VC++ call
> the value object returning function rather than inlining it. Of
> course the obvious fix is (if possible) to return a reference to the
> object instead.

> (...)
>

That's why I provided a sample with a string return value. You should
not return a reference to the objects internals.

>
> As for why the nontrivial dtor prevents inlining functions that value
> return objects, I can only speculate ... I haven't had occasion to
> look under the hood of a C++ compiler in quite a few years.
>
> My best guess is that it's too much trouble - in general - for the
> compiler to decide whether the temporary return object can be renamed
> and used directly or will need to be destructed in the caller - so
> there is a blanket edict against inlining such functions.
>

Hmmm ... VC++ will always do RVO, i.e., there will never be a temporary
object for the string object, even if all optimizations are turned off.
So I can't imagine how the temporary could pose a problem here.

> The experts in comp.compilers are likely to know more (or at least
> guess better).
>

Thanks.
I guess it's off to a third NG with the question :-)


cheers,
Martin

George Neuner

unread,
Dec 2, 2009, 12:16:20 AM12/2/09
to
On Sat, 28 Nov 2009 02:39:57 CST, "Martin B." <0xCDC...@gmx.at>
wrote:

>George Neuner wrote:
>
>> My best guess is that it's too much trouble - in general - for the
>> compiler to decide whether the temporary return object can be renamed
>> and used directly or will need to be destructed in the caller - so
>> there is a blanket edict against inlining such functions.
>
>Hmmm ... VC++ will always do RVO, i.e., there will never be a temporary
>object for the string object, even if all optimizations are turned off.
>So I can't imagine how the temporary could pose a problem here.

The compiler always _tries_ to do RVO, but there are plenty of cases
where it is not possible - one of which is passing an object by value
where a reference is expected (a Microsoft extension).

If you do something like:

string G() { ... }
string F( string &in ) { string out; ...; return out; }

string x = F( G() );

you'll find that the return value from F() is optimized as expected
but for F(G()) the return value from G() is a hidden stack temporary,
a reference to which is then passed to F().

George

Martin B.

unread,
Dec 2, 2009, 8:05:08 AM12/2/09
to
George Neuner wrote:
> On Sat, 28 Nov 2009 02:39:57 CST, "Martin B." <0xCDC...@gmx.at>
> wrote:
>
>> George Neuner wrote:
>>
>>> My best guess is that it's too much trouble - in general - for the
>>> compiler to decide whether the temporary return object can be renamed
>>> and used directly or will need to be destructed in the caller - so
>>> there is a blanket edict against inlining such functions.
>> Hmmm ... VC++ will always do RVO, i.e., there will never be a temporary
>> object for the string object, even if all optimizations are turned off.
>> So I can't imagine how the temporary could pose a problem here.
>
> The compiler always _tries_ to do RVO, but there are plenty of cases
> where it is not possible - one of which is passing an object by value
> where a reference is expected (a Microsoft extension).
>
> If you do something like:
>
> string G() { ... }
> string F( string &in ) { string out; ...; return out; }
>
> string x = F( G() );
>
> you'll find that the return value from F() is optimized as expected
> but for F(G()) the return value from G() is a hidden stack temporary,
> a reference to which is then passed to F().
>

Cheers, that's an interesting point.
One might argue though that the hidden stack temporary "belongs" to the
F(.) call and thus does say nothing about RVO. :-)
That is, given:

string G() { string s; ... returns s; }
string F(string &in) { string out; ... returns out; }

and given two different code versions:

void t3() { // uses MS extension
string s;
s = F( G() );
}

void t4() {
string s;
string tmp(G());
s = F( tmp );
}

this will essentially generate the same assembly for t3 and t4 so I'm
really not sure if we can and should say that "RVO does not happen".

br,
Martin

George Neuner

unread,
Dec 2, 2009, 5:01:02 PM12/2/09
to
On Wed, 2 Dec 2009 07:05:08 CST, "Martin B." <0xCDC...@gmx.at>
wrote:

I suppose you can make the case that the temporary somehow belongs to
F(), but in any event you can show that it isn't optimized away with
the following:

class myString : public string
{
public:
myString() { cout << '+'; }
myString( char* src ) : string( src ) { cout << '+'; }
myString( myString &src ) : string( src ) { cout << '='; }
~myString() { cout << '-'; }
};

myString GetInput( char* prompt )
{
char input[100];
cout << prompt;
cin.getline( input, 99, '\n');
return myString(input);
}

myString Find(myString &input,myString &oldtext,myString& newtext)
{
size_t where;
where = input.find( oldtext );
if ( where != string::npos )
{
input.replace( where, oldtext.length(), newtext, 0,
newtext.length() );
}
return input;
}

int main( void )
{
myString oldtext = GetInput("Find:");
myString newtext = GetInput("Replace With:");
myString text = Find( GetInput("In:"), oldtext, newtext );
cout << text << endl;
return 0;
}

I'm requiring user input so the compiler doesn't optimize objects away
by value propagation. When you run it, you'll see along with the
prompts and your input "+++=----" ... 3 normal constructions, a copy
construction and 4 destructions - proving that there is a hidden
temporary object.

You do need MS language extensions enabled to compile this.

George

0 new messages