Linkage of inline functions (revision 2) Document No: X3J16/94-0121 = WG21/N0508 Josee Lajoie, josee@vnet.ibm.com ======================================== This paper proposes resolutions for the the following questions: o What is the linkage of inline member functions? o What is the linkage of inline static member functions? o What is the linkage of inline virtual member functions? o What is the result of taking the address of an inline member function in an inline member function? o How do static local variables work inside member functions? o Can one declare global extern inline functions? Section 1 below describes what I believe is the current status quo: inline functions have internal linkage, always. Some rules are proposed to clarify some aspects of the current status quo not completely spelled out in the working paper. Section 2 describes an alternative to the current status quo that has often been presented on the reflectors: this section describes the rules needed to give inline member functions external linkage. Section 3 describes a mechanism that gives users explicit control over which inline function has external linkage. This mechanism is completely orthogonal to the rules specified in section 1 and 2 though depending on which set of rules the committee members decide to adopt, this mechanism could apply either to all inline functions (if the rules in section 1 are adopted) or only to inline functions declared in the global scope (if the rules in section 2 are adopted). The intent is to have the committee members adopt either the rules in section 1 or those in section 2 and then to decide if the additional mechanism in section 3 is also needed. 1. Clarification of the status quo =============================== After discussing the questions listed above with Bjarne and others, I believe the following rules describe the current status quo regarding inline functions. This is the behavior most C++ implementations support and the behavior most C++ users expect. o Rule 1 (R1): -------------- Inline member functions (either static or not) have internal linkage. Discussion: This rule implies that 'inline' is not just a hint to the compiler but also has semantic implications on a program. The address of an inline member function is different in different translation units. Example: // ---- file 1 ---- struct C { static void f() {} }; bool g( void (*fp)() ) { return fp == &C::f; } // ---- file 2 ---- struct C { static void f() {} }; bool g( void (*fp)() ); ... g(&f) ... Because of rule R1, the function 'g' is guaranteed to return false. Rule R1 also implies that virtual member functions declared inline have internal linkage and the address of an inline virtual member function is different in different translation units. o Rule 2 (R2): -------------- Section 7.1.2 Function Specifiers: An inline member function must have exactly the same(*) definition in every compilation in which it appears. (*) the meaning of 'the same' will be clarified when the ODR is clarified. Discussion: Because a class with external linkage must have the _same_ definition in different compilation units in which it is defined, an additional constraint must be mandated for inline member functions of classes with external linkage: Even though inline member functions of classes with external linkage have internal linkage, the definition of these inline member functions must be the _same_ in the different compilation units in which they appear. A consequence of rule R2 is that taking the address of an inline member function inside an inline member function is a violation of the ODR. Example: // ---- file 1 ---- struct C { static void f() {} static void g() { ...&f... } }; // ---- file 2 ---- struct C { static void f() {} static void g() { ...&f... } }; Here, because f has internal linkage, the address of function f in file 1 is different from the address of function f in file 2. This implies that the definition of the inline member function g in file 1 is not the same as the definition in file 2, causing R2 to be violated. Because inline virtual member functions have internal linkage, and because the address of an inline virtual member function is different in different compilation units, the content of the virtual function tables may differ from one compilation unit to another, this even though the virtual function table is for a class with external linkage. This means that implementations will sometimes be required to generate code that violates the ODR. o Rule 3 (R3): -------------- A program must have only one copy of an inline member function local static variable. Discussion: What happens with static local variables inside inline member functions is not clearly answered by the current rules in the working paper and implementations differ on how to handle them. Example: // ---- file t.C ---- extern int f1(); extern int f2(); main() { return f1() + f2(); } // ---- file t.h ---- struct X { int next() { static int count = 0; return ++count; } }; // ---- file t1.C ---- #include "t.h" int f1() { X x1; return x1.next() } // ---- file t2.C ---- #include "t.h" int f2() { X x2; return x2.next(); } Does this program return the value 2 or 3? Because the working paper is not totally clear on this topic, we have two choices: o ban local static variables inside inline member functions o force implementations to have only one copy per program for a local static variable inside an inline member function I have selected to present the proposal to support the latter option. I find the first option too restrictive and therefore less appealing. Also, Jerry Schwarz [ core-3168 ] describes an implementation mechanism that makes implementing rule 3 feasible for all implementations: Implementing it using existing simple mechanisms is not hard. "count" is treated essentially as if it were a static data member of X, with a one time flag to ensure proper initialization. There is a slight runtime penalty for situations in which you might expect static initialization, such as static int count = 1 ; Static data member's already exist. One-time flags for more complicated static local's already exist. struct A { A(); A& f(); }; A& A::f() { static A a ; // requires one-time flag return &a ; } Given Jerry's comment, I see no reason why rule 3 should not be mandated by the working paper. With rule 3, the sample program shown above returns the value 3. o Rule 4 (R4): -------------- Section 7.1.1 Function Specifiers: For a nonmember function, an inline specifier is equivalent to a static specifier for linkage purposes. Discussion: This implies that global inline functions have internal linkage. This also implies that declaring a function 'extern inline' is ill-formed because of the contradiction on the linkage specifications. Section 1 - FORMAL PROPOSAL --------------------------- Adopt R1 and R3 to clarify the behavior of inline functions. [ The current working paper already mandates R2 and R4. ] 2. Change the status quo - inline member functions have external linkage ============================================= Many have complained about the current status quo as described in section 1 above. Some of the complaints heard are as follows: o inline means more than a 'hint to the compiler' and influences the semantics of inline functions (see rules R1, R2 and R4). This prevents implementations from improving their support for inline functions. For example, the current rules prevent an implementation from making the address of an inline member function the same throughout a program, from sharing the object code for inline member functions not inlined at the point of call, this, even though these functions are "the same" throughout the whole program. o The consequences of rule R2 is too restrictive. Preventing users from taking the address of an inline member function inside other inline member functions is quite limiting. o Because of the support required for templates, C++ already requires that an implementation map many definitions of a single function in the source code into only one body in the object code. Since such mechanism is already required, there is no reason why the working paper cannot also require that this mechanism be applied to inline functions. Because of these complaints, I provide here another set of rules that I believe describes the desired behavior for inline member functions with external linkage. o Revised Rule 1 (RR1): ----------------------- Member functions have the linkage of the class that owns them. Discussion: Linkage is ascribed to a class depending on how this class is used. Inline member functions of classes with no linkage have no linkage. Inline member functions of classes with external linkage have external linkage. Example: // ---- file 1 ---- struct C { static void f() {} }; bool g( void (*fp)() ) { return fp == &C::f; } // ---- file 2 ---- struct C { static void f() {} }; bool g( void (*fp)() ); ... g(&f) ... Here, given that C is a class with external linkage, the function 'f' has external linkage and the function 'g' is guaranteed to return true. o Rule 2 (R2): -------------- [ This rule doesn't need to be modified and stays as specified in ] [ section 1. ] [ Section 7.1.2 Function Specifiers: ] [ An inline member function must have exactly the same definition in ] [ every compilation in which it appears. ] Discussion: Even though R2 is identical to how it was defined in section 1, RR1 modifies slightly the consequences of R2: taking the address of an inline member function inside an inline member function is now a well-formed operation because inline member functions of classes with external linkage have external linkage and therefore have the same address throughout the entire program. Example: // ---- file 1 ---- struct C { static void f() {} static void g() { ...&f... } }; // ---- file 2 ---- struct C { static void f() {} static void g() { ...&f... } }; Assuming C has external linkage, f also has external linkage and the address of function f in file 1 is the same as the address of function f in file 2. This implies that the definition of function g in file 1 is the same as the definition of function g in file 2. Rule R2 is respected and this program is not a violation of the ODR. Of course, RR1 also gives inline virtual member functions (of classes with external linkage) external linkage. The address of an inline virtual member function is then the same throughout the entire program, causing the content of the virtual function table for a particular class to be the same throughout the entire program. This means that implementations will not generate code that violates the ODR. o Rule 3 (R3): --------------- [ This rule doesn't need to be modified and stays as specified in ] [ section 1: ] [ A program must have only one copy of an inline member function ] [ local static variable. ] Discussion: Adopting RR1 (forcing only one copy of an inline member function in the object code of a program) will make local static variables in inline member functions behave as required in R3 automatically. o Rule 4 (R4): --------------- [ This rule doesn't need to be modified and stays as specified in ] [ section 1: ] [ A global function declared inline and not explicitly declared ] [ extern or explicitly declared static has internal linkage. ] Discussion: I do not propose to change the default linkage of functions declared inline in the global scope. The linkage for such functions should remain internal linkage. Changing the default behavior would break code. Global inline functions accessing file static data and functions or using file specific definitions, like typedef and classes, would change behavior. I don't believe that the advantages gained in having global inline functions receive external linkage by default are worth the backward compatibility problems it will cause. Section 2 - FORMAL PROPOSAL --------------------------- Adopt RR1 and R3 to clarify the behavior of inline functions. [ The current working paper already mandates R2 and R4. ] 3. Mechanism for users to control the linkage of inline functions ============================================================== o Rule 5 (R5): --------------- Using 'extern' in front of any inline function gives the function external linkage. The user must explicitly provide a non-inline definition for the function in one of the program's compilation units otherwise the program behavior is undefined. The inline function definitions and the out-of-line definition must be the same(*). (*) the meaning of 'the same' will be clarified when the ODR is clarified. Discussion: Rule R5 allows users to better control the code size of their application when inline functions are not inlined at the point of call by the implementation (for whatever reason). Let's first look at what R5 means for inline member functions. For any inline member function (static or non-static; virtual or non-virtual), it is possible to declare the function as 'extern' in the class member list to give the function external linkage: class C { public: extern void f() { ... } // C::f has external linkage }; The user must control where the function definition resides by explicitly providing an out-of-inline definition for the function C::f in one of the program's compilation unit: void C::f() { ... } C::f has external linkage but also may be inlined at the point of call if possible. Only one body for C::f will ever be generated in the object code if C::f is not inlined, that is, the address of an extern inline member function is the same throughout an entire program. For global functions, rule R5 means that a global inline function declared extern has external linkage. extern inline void f() { .... } The user must control where the function definition resides by explicitly providing an non-inline definition for the function f in one of the program's compilation unit: extern void f() { ... } f has external linkage but also may be inlined at the point of call if possible. Only one body for f will ever be generated in the object code if f is not inlined, that is, the address of a global extern inline function is the same throughout an entire program. Section 3 - FORMAL PROPOSAL 1 ----------------------------- Adopt R5 for all inline functions. [ This assumes the proposal in section 1 were adopted ] Section 3 - FORMAL PROPOSAL 2 ----------------------------- Adopt R5 for all global inline functions. [ This assumes the proposal in section 2 were adopted ] Josee.