[Moved to DR at 4/01 meeting.]
Proposed resolution (04/01): Change the first sentence of 9.12 [dcl.link] paragraph 1
from to to Add at the end of the final example on 9.12 [dcl.link] paragraph 4: Change 9.12 [dcl.link] paragraph 7 from Except for functions with internal linkage, a function
first declared in a linkage-specification behaves as a
function with external linkage. [Example:
is ill-formed (9.2.2 [dcl.stc]). ] The form of
linkage-specification that contains a braced-enclosed
declaration-seq does not affect whether the contained
declarations are definitions or not (6.2 [basic.def]); the
form of linkage-specification directly containing a
single declaration is treated as an extern specifier
(9.2.2 [dcl.stc]) for the purpose of determining whether
the contained declaration is a definition. [Example:
—end example] A linkage-specification directly
containing a single declaration shall not specify a
storage class. [Example:
—end example] to —end example]
All function types, function names, and variable names
have a language linkage.
All function types, function names with external
linkage, and variable names with external linkage have
a language linkage.
In a linkage-specification, the specified language
linkage applies to the function types of all function
declarators, function names, and variable names
introduced by the declaration(s).
In a linkage-specification, the specified language
linkage applies to the function types of all function
declarators, function names with external linkage, and
variable names with external linkage declared within
the linkage-specification.
extern "C" {
static void f4(); // the name of the function f4 has
// internal linkage (not C language
// linkage) and the function's type
// has C language linkage
}
extern "C" void f5() {
extern void f4(); // Okay -- name linkage (internal)
// and function type linkage (C
// language linkage) gotten from
// previous declaration.
}
extern void f4(); // Okay -- name linkage (internal)
// and function type linkage (C
// language linkage) gotten from
// previous declaration.
void f6() {
extern void f4(); // Okay -- name linkage (internal)
// and function type linkage (C
// language linkage) gotten from
// previous declaration.
}
extern "C" double f();
static double f(); // error
extern "C" int i; // declaration
extern "C" {
int i; // definition
}
extern "C" static void f(); // error
A declaration directly contained in a
linkage-specification is treated as if it contains the
extern specifier (9.2.2 [dcl.stc]) for the purpose of
determining the linkage of the declared name and
whether it is a definition. Such a declaration shall
not specify a storage class. [Example:
extern "C" double f();
static double f(); // error
extern "C" int i; // declaration
extern "C" {
int i; // definition
}
extern "C" static void g(); // error
Notes from 10/00 meeting: After further discussion, the core language working group
determined that the more extensive proposal described above is
not needed and that the following changes are sufficient.
Notes from 04/00 meeting: The following changes were tentatively approved, but because they
do not completely implement the proposal above the issue is being kept
for the moment in "drafting" status.
Proposed Resolution: The intent is to distingush implicit linkage from explicit linkage
for both name linkage and language (function type) linkage. (It might
be more clear to use the terms name linkage and type linkage
to distinguish these concepts. A function can have a name with one
kind of linkage and a type with a different kind of linkage. The
function itself has no linkage: it has no name, only the declaration has
a name. This becomes more obvious when you consider function pointers.) The tentatively agreed proposal is to apply implicit linkage to names
declared in brace-enclosed linkage specifications and to non-top-level
names declared in simple linkage specifications; and to apply explicit
linkage to top-level names declared in simple linkage specifications. The language linkage of any function type formed through a function
declarator is that of the nearest enclosing linkage-specification.
For purposes of determining whether the declaration of a namespace-scope
name matches a previous declaration, the language linkage portion of the
type of a function declaration (that is, the language linkage of the function
itself, not its parameters, return type or exception specification) is
ignored. For a linkage-specification using braces, i.e. For a linkage-specification without braces, i.e. the linkage of the names declared in the top-level declarators of declaration
is specified by string-literal; if this conflicts with the linkage
of any matching previous declarations, the program is ill-formed. The language
linkage of the type of any top-level function declarator is specified by
string-literal; if this conflicts with the language linkage of the
type of any matching previous function declarations, the program is ill-formed.
The effect of the linkage-specification on other (non top-level)
names declared in declaration is the same as that of the brace-enclosed
form.
Bill Gibbons: In particular, these should be well-formed: but these should not: That is, non-top-level declarators get their linkage from matching
declarations, if any, else from the nearest enclosing linkage
specification. (As already described, top-level declarators in a
brace-enclosed linkage specification get the linkage from matching
declarations, if any, else from the linkage specifcation; while
top-level declarators in direct linkage specifications get their
linkage from that specification.)
Mike Miller: This is a pretty significant change from the
current specification, which treats the two forms of language linkage
similarly for most purposes. I don't understand why it's desirable to
expand the differences. It seems very unintuitive to me that you could have a top-level
declaration in an extern "C" block that would not
receive "C" linkage. In the current standard, the statement in
9.12 [dcl.link] paragraph 4 that applies to both forms. I would thus expect that in both "C++" declarations would be well-formed, declaring an
overloaded version of f that takes a pointer to a "C++"
function as a parameter. I wouldn't expect that either declaration
would be a redeclaration (valid or invalid) of the "C" version of
f .
Bill Gibbons: The potential difficulty is the matching
process and the handling of deliberate overloading based on language
linkage. In the above examples, how are these two declarations
matched: given that the linkage that is part of fp1 is "C" while the linkage (prior
to the matching process) that is part of fp2 is "C++"? The proposal is that the linkage which is part of the parameter type is not
determined until after the match is attempted. This almost always correct
because you can't overload "C" and "C++" functions; so if the function names
match, it is likely that the declarations are supposed to be the
same.
Mike Miller: This seems like more trouble than it's worth.
This comparison of function types ignoring linkage specifications is,
as far as I know, not found anywhere in the current standard. Why do
we need to invent it?
Bill Gibbons: It is possible to construct pathological cases where this fails, e.g. It is reasonable to require explicit typedefs in this case so that
in the above example the second function declaration gets its parameter type
function linkage from the first function declaration. (In fact, I think you can't get into this situation without having already
used typedefs to declare different language linkage for the top-level and
parameter linkages.) For example, if the intent is to overload based on linkage a
typedef is needed: In this case the two function declarations refer to different
functions.
Mike Miller: This seems pretty strange to me. I think it
would be simpler to determine the type of the parameter based on the
containing linkage specification (implicitly "C++") and require a
typedef if the user wants to override the default behavior. For
example:
extern string-literal { declaration-seqopt
}
the linkage of any declaration of a namespace-scope name (including local
externs) which is not contained in a nested linkage-specification,
is not declared to have no linkage (static), and does not match a previous
declaration is given the linkage specified in the string-literal.
The language linkage of the type of any function declaration of a namespace-scope
name (including local externs) which is not contained in a nested linkage-specification
and which is declared with function declarator syntax is the same as that
of a matching previous declaration, if any, else is specified by string-literal.
extern string-literal declaration
extern "C" void f(void (*fp)()); // parameter type is pointer to
// function with C language linkage
extern "C++" void g(void (*fp)()); // parameter type is pointer to
// function with C++ language linkage
extern "C++" { // well-formed: the linkage of "f"
void f(void(*fp)()); // and the function type used in the
} // parameter still "C"
extern "C" { // well-formed: the linkage of "g"
void g(void(*fp)()); // and the function type used in the
} // parameter still "C++"
extern "C++" void f(void(*fp)()); // error - linkage of "f" does not
// match previous declaration
// (linkage of function type used in
// parameter is still "C" and is not
// by itself ill-formed)
extern "C" void g(void(*fp)()); // error - linkage of "g" does not
// match previous declaration
// (linkage of function type used in
// parameter is still "C++" and is not
// by itself ill-formed)
the specified language linkage applies to the function types of all
function declarators, function names, and variable names introduced by
the declaration(s)
extern "C" void f(void(*)());
extern "C++" {
void f(void(*)());
}
extern "C++" f(void(*)());
extern "C" void f(void (*fp1)());
extern "C++" {
void f(void(*fp2)());
}
extern "C" typedef void (*PFC)(); // pointer to "C" linkage function
void f(PFC); // parameter is pointer to "C" function
void f(void (*)()); // matching declaration or overload based on
// difference in linkage type?
extern "C" typedef void (*PFC)(); // pointer to "C" linkage function
void f(PFC); // parameter is pointer to "C" function
typedef void (*PFCPP)(); // pointer to "C++" linkage function
void f(PFCPP); // parameter is pointer to "C++" function
extern "C" {
typedef void (*PFC)(); // pointer to "C" function
void f(void(*)()); // takes pointer to "C" function
}
void f(void(*)()); // new overload of "f", taking
// pointer to "C++" function
void f(PFC); // redeclare extern "C" version
9.12 [dcl.link] paragraph 6
says the following:
Suggested resolution: The extern "C" linkage specification applies
only to the type of functions with internal linkage, and therefore some
of the rules that have to do with name overloading don't apply.At most one of a set of overloaded functions with a particular name
can have C linkage.
Does this apply to static functions as well? For example, is the following
well-formed?
extern "C" {
static void f(int) {}
static void f(float) {}
};
Can a function with internal linkage "have C linkage" at all (assuming
that phrase means "has extern "C" linkage"), for how can a function be
extern "C" if it's not extern? The function type can have extern
"C" linkage — but I think that's independent of the linkage of the function
name. It should be perfectly reasonable to say, in the example above,
that extern "C" applies only to the types of f(int) and f(float) ,
not to the function names, and that the rule in 9.12 [dcl.link] paragraph 6
doesn't apply.