Static local objects in inline functions.

  • Follow


Hello!

I'm looking for a little clarification about the behavior of static local
objects in inline functions. Consider the following program consisting of
three files. First the header, check.hpp:


#include <iostream>

// Quick and dirty class with a noisy constructor.
class X {
public:
     X( ) { std::cout << "I'm in X::X( )\n"; }
};

// This is the interesting function.
inline void f( )
{
     static X object;
}

extern void g( );


Next the file check.cpp containing the main function:

#include "check.hpp"

int main( )
{
     f( );  // Call the inline function.
     g( );  // Hop over to another translation unit.
}


Finally the file check2.cpp containing function g():

#include "check.hpp"

void g( )
{
     f( );  // Call the inline function here too.
}


When compiled with g++ v4.3.2 the constructor is only called once. However
some other compilers call the constructor twice. In those cases, it appears
that the static local object inside f( ) is duplicated in each translation
unit (and is constructed once in each translation unit).

Section 9.3p6 of the 2003 standard says, "A static local variable in a member
function always refers to the same object, whether or not the member function
is inline." That seems fairly clear except that I'm not 100% sure what is
meant by "the same object" in this context. When the definition of the inline
function appears in two different translation units, is it the same object in
each translation unit?

Section 7.1.2p4 says, "An inline function shall be defined in every
translation unit in which it is used and shall have exactly the same
definition in every case (3.2)"

Section 3.2 is the One Definition Rule. Section 3.2p5 says, "There can be more
than one definition of a... inline function with external linkage (7.1.2)...
provided that each definition appears in a different translation unit, and
provided the definitions satisfy the following requirements..."

One of those requirements says, "... in each definition of D [the entity with
multiple definitions], corresponding names, looked up according to 3.4, shall
refer to an entity defined within the definition of D, or shall refer to the
same entity..."

To me this leaves the central question unanswered. 9.3p6 says that static
local variables of inline functions are the "same object" and the description
of the ODR says that two definitions can exist in separate translation units
if they make use of the "same entity." Are the objects the same or not?

My question comes down to this... which of these possibilities is correct:

1. A conforming compiler must call X::X exactly once in my sample program
above. There is only a single object program-wide representing the static
local variable.

2. A conforming compiler must call X::X exactly twice in my sample program
above. There is a single object in each translation unit representing the
static local variable.

3. The number of calls to X::X is unspecified.

4. The program has UB.

Peter


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

0
Reply pcc482719 (137) 4/18/2010 12:29:15 AM

On 4/17/10 11:29 PM, Peter C. Chapin wrote:
> Hello!
>
> I'm looking for a little clarification about the behavior of static local
> objects in inline functions. Consider the following program consisting of
> three files. First the header, check.hpp:
>
>
> #include<iostream>
>
> // Quick and dirty class with a noisy constructor.
> class X {
> public:
>       X( ) { std::cout<<  "I'm in X::X( )\n"; }
> };
>
> // This is the interesting function.
> inline void f( )
> {
>       static X object;
> }
>
> extern void g( );
>
>
> Next the file check.cpp containing the main function:
>
> #include "check.hpp"
>
> int main( )
> {
>       f( );  // Call the inline function.
>       g( );  // Hop over to another translation unit.
> }
>
>
> Finally the file check2.cpp containing function g():
>
> #include "check.hpp"
>
> void g( )
> {
>       f( );  // Call the inline function here too.
> }
>
>
> When compiled with g++ v4.3.2 the constructor is only called once. However
> some other compilers call the constructor twice. In those cases, it appears
> that the static local object inside f( ) is duplicated in each translation
> unit (and is constructed once in each translation unit).
>
> Section 9.3p6 of the 2003 standard says, "A static local variable in a member
> function always refers to the same object, whether or not the member function
> is inline."


Your f() is not a member function.


-=JL=-


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

0
Reply Jonathan 4/18/2010 9:12:18 AM



"Peter C. Chapin" <pcc482719@gmail.com> wrote in message 
news:4bca60a8$0$2373$4d3efbfe@news.sover.net...
> Hello!
>
> I'm looking for a little clarification about the behavior of static local
> objects in inline functions. Consider the following program consisting of
> three files. First the header, check.hpp:
<snip>

Inline functions have external linkage by default and in this form the 
static local should only be created once no matter how many TUs use it.  If 
you make the inline function have internal linkage you will get a separate 
static object in each TU that uses it.

/Leigh 


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

0
Reply Leigh 4/18/2010 9:17:21 AM

On 18 Apr., 08:29, "Peter C. Chapin" <pcc482...@gmail.com> wrote:
> Hello!
>
> I'm looking for a little clarification about the behavior of static local
> objects in inline functions. Consider the following program consisting of
> three files. First the header, check.hpp:
>
> #include <iostream>
>
> // Quick and dirty class with a noisy constructor.
> class X {
> public:
>      X( ) { std::cout << "I'm in X::X( )\n"; }
>
> };
>
> // This is the interesting function.
> inline void f( )
> {
>      static X object;
>
> }
>
> extern void g( );
>
> Next the file check.cpp containing the main function:
>
> #include "check.hpp"
>
> int main( )
> {
>      f( );  // Call the inline function.
>      g( );  // Hop over to another translation unit.
>
> }
>
> Finally the file check2.cpp containing function g():
>
> #include "check.hpp"
>
> void g( )
> {
>      f( );  // Call the inline function here too.
>
> }
>
> When compiled with g++ v4.3.2 the constructor is only called once. However
> some other compilers call the constructor twice. In those cases, it appears
> that the static local object inside f( ) is duplicated in each translation
> unit (and is constructed once in each translation unit).

The standard allows only a single constructor call, except if
that constructor call would fail via an exception. I exclude
this case, because there is nothing in your program which
would allow for a second call of the function and it would
terminate in this case.

> Section 9.3p6 of the 2003 standard says, "A static local variable in a member
> function always refers to the same object, whether or not the member function
> is inline." That seems fairly clear except that I'm not 100% sure what is
> meant by "the same object" in this context. When the definition of the inline
> function appears in two different translation units, is it the same object in
> each translation unit?

The same object just means that the multiple definitions
of the inline member function which contain the static
local variable shall behave as if this static local
object is the same object for each function. This again
implies via the initialization rules of static local
objects that it shall only be initialized *once*, see
6.7/4:

"[..] Otherwise such an object is initialized the first
time control passes through its declaration; such an
object is considered initialized upon the completion of
its initialization. [..]"

This makes pretty clear that a given local static object
can only be initialized once, but note that your quoted
wording of 9.3p6 is restricted to *member functions. The
correct quote relevant for your example is 7.1.2/4:

"[..] A static local variable in an extern inline function
always refers to the same object. [..]"

> Section 7.1.2p4 says, "An inline function shall be defined in every
> translation unit in which it is used and shall have exactly the same
> definition in every case (3.2)"
>
> Section 3.2 is the One Definition Rule. Section 3.2p5 says, "There can be more
> than one definition of a... inline function with external linkage (7.1.2)...
> provided that each definition appears in a different translation unit, and
> provided the definitions satisfy the following requirements..."
>
> One of those requirements says, "... in each definition of D [the entity with
> multiple definitions], corresponding names, looked up according to 3.4, shall
> refer to an entity defined within the definition of D, or shall refer to the
> same entity..."

The ODR-related requirements just make clear, that inline
functions may (and need) to be defined in each translation
unit where they are used. The remaining parts you quoted
from this sub-clause just impose the requirement that
you don't trick here, e.g.:

#ifdef SOME_THING
#  define TT int;
#else
#  define TT long;
#endif

inline double foo() {
  TT var = 0;
  return var;
}

The wording requires that TT is always the same
in each translation unit where foo is defined,
otherwise this would cause undefined behaviour.
But this problem is not part of your example.

> To me this leaves the central question unanswered. 9.3p6 says that static
> local variables of inline functions are the "same object" and the description
> of the ODR says that two definitions can exist in separate translation units
> if they make use of the "same entity." Are the objects the same or not?

9.3 is only referring to member functions,
as shown above, but that is not relevant:
A function local static object has to be
the same for each inline function by
7.1.2/4 and the initialization rules
quoted above require that this object
is only required once successfully. The
ODR does not conflict with this: It
concentrates on other aspects that could
possibly have the effect that the definitions
are not the same. In this regard does the
ODR not say something special about local
static objects.

> My question comes down to this... which of these possibilities is correct:
>
> 1. A conforming compiler must call X::X exactly once in my sample program
> above. There is only a single object program-wide representing the static
> local variable.
>
> 2. A conforming compiler must call X::X exactly twice in my sample program
> above. There is a single object in each translation unit representing the
> static local variable.
>
> 3. The number of calls to X::X is unspecified.
>
> 4. The program has UB.

The only valid solution is 1, assuming that the
implementation of the constructor does not throw
an exception.

HTH & Greetings from Bremen,

Daniel Kr�gler


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

0
Reply ISO 4/18/2010 9:18:17 AM

Daniel Krügler wrote:

> "[..] Otherwise such an object is initialized the first
> time control passes through its declaration; such an
> object is considered initialized upon the completion of
> its initialization. [..]"
>
> This makes pretty clear that a given local static object
> can only be initialized once, but note that your quoted
> wording of 9.3p6 is restricted to *member functions. The
> correct quote relevant for your example is 7.1.2/4:
>
> "[..] A static local variable in an extern inline function
> always refers to the same object. [..]"

Thanks to you and everyone else for your reply. I can see that the notion of
an *external* inline function helps to clarify things.

Actually in my original code, the inline function was a static member of a
class like this

class Y {
public:
   static void f( )
   {
      static X object;
   }
};


But the issue seems to be the same for "ordinary" inline functions and that
case seemed easier to think about. I located 9.3p6 initially and forgot to
update the quote when I changed the example. :)

> The ODR-related requirements just make clear, that inline
> functions may (and need) to be defined in each translation
> unit where they are used. The remaining parts you quoted
> from this sub-clause just impose the requirement that
> you don't trick here, e.g.:

Yes, I understand this issue. My confusion was in the contrast between the
following cases. Consider when everything is in one translation unit.

inline void f( )
{
   static X object;
}


void g( )
{
   f( );
}

void h( )
{
   f( );
}

The standard seems clear that both invocations of f( ) share the same static
local object despite the fact that f( ) is expanded inline. On the other hand
consider this case:

---> check.hpp
static int x;  // An internally linked object declared in a header file.

Now every translation unit that includes check.hpp will get its own private
version of x. If we wrap x in an inline function... then what? However, I can
appreciate that an externally linked inline function should behave, in some
sense, "as if" it was defined in some implementation file. That is, such
functions are defined in header files only as a concession to limited
compiler technology. If the inline function was defined in an implementation
file, it would be clearer (to me) that should be only one copy of the static
local variable.

I suppose ultimately my confusion is due to the confusing use of 'static' to
control linkage in some cases and duration in other cases.

Peter


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

0
Reply Peter 4/18/2010 4:02:02 PM

On 19 Apr., 00:02, "Peter C. Chapin" <pcc482...@gmail.com> wrote:

[..]

> My confusion was in the contrast between the
> following cases. Consider when everything is in one translation unit.
>
> inline void f( )
> {
>    static X object;
> }
>
> void g( )
> {
>    f( );
> }
>
> void h( )
> {
>    f( );
> }
>
> The standard seems clear that both invocations of f( ) share the same static
> local object despite the fact that f( ) is expanded inline.

Exactly.

> On the other hand consider this case:
>
> ---> check.hpp
> static int x;  // An internally linked object declared in a header file.
>
> Now every translation unit that includes check.hpp will get its own private
> version of x. If we wrap x in an inline function... then what?

If you wrap x in an inline function, it becomes
an object of no linkage but it still has static
storage duration. The keyword static has a
different meaning here as if used in namespace-
scope. Note the difference compared to the namespace-
scope object x above which has *internal* linkage.

> However, I can
> appreciate that an externally linked inline function should behave, in some
> sense, "as if" it was defined in some implementation file. That is, such
> functions are defined in header files only as a concession to limited
> compiler technology. If the inline function was defined in an implementation
> file, it would be clearer (to me) that should be only one copy of the static
> local variable.
>
> I suppose ultimately my confusion is due to the confusing use of 'static' to
> control linkage in some cases and duration in other cases.

Exactly this is the reason for the misinterpretation.

HTH & Greetings from Bremen,

Daniel Kr�gler


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

0
Reply ISO 4/18/2010 10:48:11 PM

5 Replies
109 Views

(page loaded in 0.16 seconds)


Reply: