| Document number: | J16/01-0056 = WG21 N1342 |
| Date: | 9 November, 2001 |
| Project: | Programming Language C++ |
| Reference: | ISO/IEC IS 14882:1998(E) |
| Reply to: | J. Stephen Adamczyk |
| jsa@edg.com |
This document contains the C++ core language issues that have been categorized as Defect Reports by the C++ Standard Committee (J16 + WG21), along with their proposed resolutions. THESE RESOLUTIONS ARE NOT YET PART OF THE INTERNATIONAL STANDARD FOR C++. They are provided for informational purposes only, as an indication of the intent of the Committee. They should not be considered definitive until or unless they appear in an approved Technical Corrigendum or revised International Standard for C++.
This document is part of a group of related documents that together describe the issues that have been raised regarding the C++ Standard. The other documents in the group are:
For more information, including a description of the meaning of the issue status codes and instructions on reporting new issues, please see the Active Issues List.
22.2.1.1.2 lib.locale.ctype.virtuals paragraph 13 states a constraint on the values of the characters representing the decimal digits in the execution character set:
for any digit character c, the expression (do_narrow( c, dfault)-'0') evaluates to the digit value of the character.This requirement is not reflected in the description of the execution character set (2.2 lex.charset paragraph 3).
Proposed resolution (10/00):
In 2.2 lex.charset paragraph 3, after the sentence
For each basic execution character set, the values of the members shall be non-negative and distinct from one another.insert the following:
In both the source and execution basic character sets, the value of each character after 0 in the above list of decimal digits shall be one greater than the value of the previous.
From reflector message core-7838.
Footnotes 26 and 29 both use the phrase "following the function declarator" incorrectly: the function declarator includes the parameter list, but the footnotes make clear that they intend what's said to apply to names inside the parameter list. Presumably the phrase should be "following the function declarator-id."
Proposed Resolution (04/99): Change the text in 3.4.1 basic.lookup.unqual paragraph 6 from:
A name used in the definition of a function [footnote: This refers to unqualified names following the function declarator; such a name may be used as a type or as a default argument name in the parameter-declaration-clause, or may be used in the function body. end footnote] that is ...to:
A name used in the definition of a function following the function's declarator-id [footnote: This refers to unqualified names that occur, for instance, in a type or default argument expression in the parameter-declaration-clause or used in the function body. end footnote] that is ...Change the text in 3.4.1 basic.lookup.unqual paragraph 8 from:
A name used in the definition of a function that is a member function (9.3 class.mfct ) [footnote: That is, an unqualified name following the function declarator; such a name may be used as a type or as a default argument name in the parameter-declaration-clause, or may be used in the function body, or, if the function is a constructor, may be used in the expression of a mem-initializer. end footnote] of class X shall be ...to:
A name used in the definition of a member function (9.3 class.mfct ) of class X following the function's declarator-id [footnote: That is, an unqualified name that occurs, for instance, in a type or default argument expression in the parameter-declaration-clause, in the function body, or in an expression of a mem-initializer in a constructor definition. end footnote] shall be ...
The example in 3.4.1 basic.lookup.unqual paragraph 3 is incorrect:
typedef int f;
struct A {
friend void f(A &);
operator int();
void g(A a) {
f(a);
}
};
Regardless of the resolution of other issues concerning the lookup
of names in friend declarations, this example is ill-formed
(the function and the typedef cannot exist in the same scope).
One possible repair of the example would be to make f a class with a constructor taking either A or int as its parameter.
(See also issues 95, 136, 138, 143, 165, and 166.)
Proposed resolution (04/01):
Change the example in 3.4.1 basic.lookup.unqual paragraph 3 to read:
typedef int f;
namespace N {
struct A {
friend int f(A &);
operator int();
void g(A a) {
int i = f(a);
// f is the typedef, not the friend function:
// equivalent to int(a)
}
};
}
Delete the sentence immediately following the example:
The expression f(a) is a cast-expression equivalent to int(a).
From reflector message core-7768.
If an argument used for lookup is the address of a group of overloaded functions, are there any associated namespaces or classes? What if it's the address of a function template?
My inclination is to say no to both.
From Mike Miller:
We discussed this on the reflector a few weeks ago. I'll leave the template case for the Core III experts, but I'd find it surprising if the overload case weren't handled as the obvious generalization of the single-function case. For a single function, the associated namespaces are those of the types used in the parameters and return type; I would expect that using an overloaded function name would simply be the union of the namespaces from the members of the overload set. That would be the simplest and most intuitive, IMHO — is there an argument for doing it differently?
Proposed Resolution (04/99): In 3.4.2 basic.lookup.koenig paragraph 2, add following the last bullet in the list of associated classes and namespaces for various argument types (not a bullet itself because overload sets and templates do not have a type):
In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set: the namespace in which the function or function template is defined and the classes and namespaces associated with its (non-dependent) parameter types and return type.
From reflector message core-7952.
Section 3.4.2 basic.lookup.koenig includes the following:
struct A {
union U {};
friend void f(U);
};
struct B {
struct S {};
friend void f(S);
};
int main() {
A::U u;
f(u); // okay: A is an associated class
B::S s;
f(s); // error: no matching f(), B is not an associated class
}
Certainly the enclosing class should also be an associated class for nested
class types, shouldn't it?
Proposed Resolution (10/99): Change the two referenced bullets to read:
The description of Koenig lookup in 3.4.2 basic.lookup.koenig paragraph 1 says,
...other namespaces not considered during the usual unqualified lookup (3.4.1 basic.lookup.unqual ) may be searched.Does this mean that Koenig lookup does not search namespaces that were already searched during the usual unqualified lookup? The answer is academic except for the two-stage lookup during template instantiation. If a given namespace is searched in the context of the template definition, are declarations in that namespace in the instantiation context ignored during the Koenig lookup? For instance,
void f(int);
template <class T> void g(T t) {
f(t);
}
enum E { e };
void f(E);
void h() {
g(e);
}
In this example, the call f(t) in the template function will
resolve to f(E) if Koenig lookup reexamines already-searched
namespaces and to f(int) if not.
Proposed Resolution (10/00):
Immediately preceding the example at the end of 3.4.2 basic.lookup.koenig paragraph 2, add the following:
[Note: the namespaces and classes associated with the argument types can include namespaces and classes already considered by the ordinary unqualified lookup.]
In 3.4.4 basic.lookup.elab paragraph 3, there is the example
struct Base {
// ...
struct Data { /* ... */ }; // Defines nested Data
struct Data; // OK: Redeclares nested Data
};
The final redeclaration is invalid according to
9.2
class.mem paragraph 1 last sentence.
Proposed resolution (10/00): Remove the line
struct Data; // OK: Redeclares nested Data
See also Core issue 36 and Core issue 56.
From reflector message 8600.
3.5 basic.link paragraph 4 says (among other things):A name having namespace scope has external linkage if it is the name ofThat prohibits for example:
- [...]
- a named enumeration (7.2 dcl.enum), or an unnamed enumeration defined in a typedef declaration in which the enumeration has the typedef name for linkage purposes (7.1.3 dcl.typedef)
typedef enum { e1 } *PE;
void f(PE) {} // Cannot declare a function (with linkage) using a
// type with no linkage.
However, the same prohibition was not made for class scope types. Indeed, 3.5 basic.link paragraph 5 says:
In addition, a member function, static data member, class or enumeration of class scope has external linkage if the name of the class has external linkage.
That allows for:
struct S {
typedef enum { e1 } *MPE;
void mf(MPE) {}
};
My guess is that this is an unintentional consequence of 3.5 basic.link paragraph 5, but I would like confirmation on that.
Proposed resolution:
Change text in 3.5 basic.link paragraph 5 from:
In addition, a member function, static data member, class or enumeration of class scope has external linkage if the name of the class has external linkage.to:
In addition, a member function, a static data member, a named class or enumeration of class scope, or an unnamed class or enumeration defined in a class-scope typedef declaration such that the class or enumeration has the typedef name for linkage purposes (7.1.3 dcl.typedef), has external linkage if the name of the class has external linkage.
A reference is rebindable. This is surprising and unnatural. This can also cause subtle optimizer bugs.Example:
struct T { int& ri; T (int& r) : ri (r) { } }; void bar (T*); void foo () { int i; T x (i); x.ri = 3; // the optimizer understands that this is really i = 3 bar (&x); x.ri = 4; // optimizer assumes that this writes to i, but this is incorrect } int gi; void bar (T* p) { p->~T (); new (p) T (gi); }If we replace T& with T* const in the example then undefined behavior result and the optimizer is correct.Proposal: make T& equivalent to T* const by extending the scope of 3.8 basic.life paragraph 9 to references.
(See also J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")
In addition, Lisa Lippincott pointed out the following example:
void f( const bool * );
void g();
int main() {
const bool *b = new const bool( false );
f(b);
if (*b)
g();
}
void f( const bool *b ) {
new ( const_cast<bool *>(b) ) const bool( true );
}
The proposed wording in the paper would still permit this usage and thus prevent an optimizer from eliminating the call to g().
Proposed Resolution (10/00):
Add a new bullet to the list of restrictions in 3.8 basic.life paragraph 7, following the second bullet ("the new object is of the same type..."):
From reflector message core-7956.
The text of 3.8 basic.life paragraph 2 currently reads,
The phrase "an object of type" is obviously incorrect. I believe it should read "an object of POD type." Does anyone disagree?
Proposed Resolution (10/99): As suggested.
From reflector message core-7850.
Can you use memcpy on non-member POD subobjects of non-POD objects?
In 3.9 basic.types paragraphs 2 and 3 we have:
For any complete POD object type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7 intro.memory ) making up the object can be copied into an array of char or unsigned char*. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [Example elided]Paragraph 3 doesn't repeat the restriction of paragraph 2. Should it be assumed? Otherwise only complete POD types are copyable to an array of char and back, but scribbling over subobjects is OK. (Or perhaps a "distinct T object" is a complete object...)*[Footnote: By using, for example, the library functions (17.4.1.2 lib.headers ) memcpy or memmove. end footnote]For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, if the value of obj1 is copied into obj2, using the memcpy library function, obj2 shall subsequently hold the same value as obj1.
Proposed Resolution (04/99): Change the text in 3.9 basic.types paragraph 2 from:
For any complete POD object type T, ...to:
For any object (other than a base class subobject) of POD type T, ...Change the text in 3.9 basic.types paragraph 3 from:
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2,to:
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base class subobject, ...
The Standard uses confusing terminology when referring to accessibility in connection with ambiguity. For instance:
4.10 conv.ptr paragraph 3:
If B is an inaccessible or ambiguous base ...5.2.7 expr.dynamic.cast paragraph 8:
... has an unambiguous public base ...10.3 class.virtual paragraph 5:
... is an unambiguous direct or indirect base ... and is accessible ...15.3 except.handle paragraph 3:
not involving conversions to pointers to private or protected or ambiguous classes
The phrase "unambiguous public base" is unfortunate as it could mean either "an unambiguous base not considering accessibility, which is public" or "an unambiguous base considering only the publicly accessible bases." I believe the former interpretation correct, as accessibility is applied after visibility (11 class.access paragraph 4) and ambiguity is described in terms of visibility (10.2 class.member.lookup paragraph 2).
Suggested Resolution: Use the phrases "public and unambiguous," "accessible and unambiguous," "non-public or ambiguous," or "inaccessible or ambiguous" as appropriate.
Proposed resolution (10/00):
From reflector message core-8091.
5.1 expr.prim paragraph 11 reads,
A template-id shall be used as an unqualified-id only as specified in 14.7.2 temp.explicit , 14.7 temp.spec , and 14.5.4 temp.class.spec .
What uses of template-ids as unqualified-ids is this supposed to prevent? And is the list of referenced sections correct/complete? For instance, what about 14.8.1 temp.arg.explicit, "Explicit template argument specification?" Does its absence from the list in 5.1 expr.prim paragraph 11 mean that "f<int>()" is ill-formed?
This is even more confusing when you recall that unqualified-ids are contained in qualified-ids:
qualified-id: ::opt nested-name-specifier templateopt unqualified-id
Is the wording intending to say "used as an unqualified-id that is not part of a qualified-id?" Or something else?
Proposed resolution (10/00):
Remove the referenced sentence altogether.
From reflector message core-8092.
The cross-reference is incorrect in the first sentence after the grammar in 5.1 expr.prim paragraph 7:A nested-name-specifier that names a class, optionally followed by the keyword template (14.8.1 temp.arg.explicit ), ...The use of the template keyword in this context is discussed in 14.2 temp.names , not 14.8.1 temp.arg.explicit .
From paper J16/99-0010 = WG21 N1187.
5.1 expr.prim paragraph 7 says that class-name::class-name names the constructor when both class-name refer to the same class. (Note the different perspective, at least, in 12.1 class.ctor paragraph 1, in which constructors have no names and are recognized by syntactic context rather than by name.)
This formulation does not address the case of classes in which a function template is declared as a constructor, for example:
template <class T> struct A {
template <class T2> A(T2);
};
template<> template<> A<int>::A<int>(int);
Here there is an ambiguity as to whether the second template argument list is for the injected class name or for the constructor.
Suggested resolution: restate the rule as a component of name lookup. Specifically, if when doing a qualified lookup in a given class you look up a name that is the same as the name of the class, the entity found is the constructor and not the injected class name. In all other cases, the name found is the injected class name. For example:
class B { };
class A: public B {
A::B ab; // B is the inherited injected B
A::A aa; // Error: A::A is the constructor
};
Without this rule some very nasty backtracking is needed. For example, if the injected class name could be qualified by its own class name, the following code would be well-formed:
template <class T> struct A {
template <class T2> A(T2);
static A x;
};
template<> A<int>::A<int>(A<int>::x);
Here the declarator for the definition of the static data member has redundant parentheses, and it's only after seeing the declarator that the parser can know that the second A<int> is the injected class name rather than the constructor.
Proposed resolution (10/00):
In 9 class paragraph 2, change
The class-name is also inserted into the scope of the class itself. For purposes of access checking the inserted class name...
to
The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name...
Also, in 3.4.3.1 class.qual, add the following before paragraph 2:
If the nested-name-specifier nominates a class C, and the
name specified after the nested-name-specifier, when looked up in
C, is the injected-class-name of C (clause
9
class), the name is instead considered
to name the constructor of class C. Such a constructor name
shall only be used in the declarator-id of a constructor
definition that appears outside of the class definition.
[Example:
struct A { A(); };
struct B: public A { B(); };
A::A() { }
B::B() { }
B::A ba; // object of type A
A::A a; // error, A::A is not a type name
—end example]
Also, change 3.4 basic.lookup paragraph 3 from
Because the name of a class is inserted in its class scope (clause 9 class), the name of a class is also considered a member of that class for the purposes of name hiding and lookup.
to
The injected-class-name of a class (clause 9 class) is also considered to be a member of that class for the purposes of name hiding and lookup.
(See also issue 194.)
From reflector messages 8046-8047.
Christophe de Dinechin: In 5.2.2 expr.call , paragraph 2 reads:
If no declaration of the called function is visible from the scope of the call the program is ill-formed.I think nothing there or in the previous paragraph indicates that this does not apply to calls through pointer or virtual calls.
Mike Miller: "The called function" is unfortunate phraseology; it makes it sound as if it's referring to the function actually called, as opposed to the identifier in the postfix expression. It's wrong with respect to Koenig lookup, too (the declaration need not be visible if it can be found in a class or namespace associated with one or more of the arguments).
In fact, this paragraph should be a note. There's a general rule that says you have to find an unambiguous declaration of any name that is used (3.4 basic.lookup paragraph 1); the only reason this paragraph is here is to contrast with C's implicit declaration of called functions.
Proposed resolution:
Change section 5.2.2 expr.call paragraph 2 from:If no declaration of the called function is visible from the scope of the call the program is ill-formed.to:
[Note: if a function or member function name is used, and name lookup (3.4 basic.lookup) does not find a declaration of that name, the program is ill-formed. No function is implicitly declared by such a call. ]
(See also issue 218.)
5.2.5 expr.ref paragraph 4 should make it clear that when a nonstatic member is referenced in a member selection operation, the type of the left operand is implicitly cast to the naming class of the member. This allows for the detection of access and ambiguity errors on that implicit cast.
Proposed Resolution (10/00):
In 11.2 class.access.base paragraph 4, remove the following from the second note:
If the member m is accessible when named in the naming class according to the rules below, the access to m is nonetheless ill-formed if the type of p cannot be implicitly converted to type T (for example, if T is an inaccessible base class of p's class).
Add the following as a new paragraph 5 of 11.2 class.access.base:
If a class member access operator, including an implicit "this->," is used to access a nonstatic data member or nonstatic member function, the reference is ill-formed if the left operand (considered as a pointer in the "." operator case) cannot be implicitly converted to a pointer to the naming class of the right operand. [Note: this requirement is in addition to the requirement that the member be accessible as named.]
In 11.2 class.access.base paragraph 4, fix a typographical error by adding the missing right parenthesis following the text
(including cases where an implicit "this->" is added
Add following the first sentence of 5.2.2 expr.call paragraph 4:
If the function is a nonstatic member function, the "this" parameter of the function (9.3.2 class.this) shall be initialized with a pointer to the object of the call, converted as if by an explicit type conversion (5.4 expr.cast). [Note: there is no access checking on this conversion; the access checking is done as part of the (possibly implicit) class member access operator. See 11.2 class.access.base.]
Section 5.2.9 expr.static.cast paragraph 6 should make it clear that when any of the "inverse of any standard conversion sequence" static_casts are done, the operand undergoes the lvalue-to-rvalue conversions first.
Proposed Resolution (10/00):
In 5.2.9 expr.static.cast paragraph 6, change
can be performed explicitly using static_cast subject to the restriction that the explicit conversion does not cast away constness (5.2.11 expr.const.cast), ...
to
can be performed explicitly using static_cast. The lvalue-to-rvalue (4.1 conv.lval), array-to-pointer (4.2 conv.array), and function-to-pointer (4.3 conv.func) conversions are applied to the operand. Such a static_cast is subject to the restriction that it does not cast away constness (5.2.11 expr.const.cast), ...
From reflector messages core-8096 through 8100.
According to 7.2 dcl.enum paragraph 9, it is permitted to convert from one enumeration type to another. However, neither 5.2.9 expr.static.cast nor 5.4 expr.cast allows this conversion.
Proposed resolution (10/00): Change the first two sentences of 5.2.9 expr.static.cast paragraph 7 to read
A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2 dcl.enum ).
From reflector message core-8198.
According to 5.2.9 expr.static.cast paragraph 10,
An rvalue of type "pointer to cv void" can be explicitly converted to a pointer to object type.No requirements are stated regarding the cv-qualification of the pointer to object type. Contrast this with the formula used in paragraphs 5, 8, and 9, where the treatment of cv-qualification is explicit, requiring that the target type be at least as cv-qualified as the source. There is an apparently general requirement on all forms of static_cast in 5.2.9 expr.static.cast paragraph 1 that it "shall not cast away constness." Assuming that this restriction applies to paragraph 10, since there is no explicit exception to the general rule, that still leaves open the question of whether one can "cast away volatility" in a conversion from volatile void* to a pointer to object type. Should 5.2.9 expr.static.cast paragraph 10 be rewritten to handle cv-qualification in the same way as paragraphs 5, 8, and 9?
Proposed resolution (10/00):
Change the first sentence of 5.2.9 expr.static.cast paragraph 10 to
An rvalue of type "pointer to cv1 void" can be converted to an rvalue of type "pointer to cv2 T", where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.
From reflector message core-7911.
5.3.4 expr.new paragraph 6 says:
The expression in a direct-new-declarator shall have integral type (3.9.1 basic.fundamental ) with a non-negative value.I assume the intent was to also allow enumeral types, as we do in 5.2.1 expr.sub ?
Proposed Resolution (10/99): Replace "integral type" by "integral or enumeration type" in 5.3.4 expr.new paragraph 6.
If a placement allocation function has default arguments for all its parameters except the first, it can be called using non-placement syntax. In such a case, it is not clear whether the deallocation function to be called if the constructor terminates by throwing an expression is determined on the basis of the syntax of the new-expression (i.e., a non-placement deallocation function) or the declaration of the selected (placement) allocation function. 5.3.4 expr.new paragraph 19 indicates that the deallocation function must match the declaration of the allocation function. However, 15.2 except.ctor says that the distinction is based on whether the new-expression contains a new-placement or not.
Proposed resolution (10/00):
In 15.2 except.ctor paragraph 2, replace
If the object or array was allocated in a new-expression and the new-expression does not contain a new-placement, the deallocation function (3.7.3.2 basic.stc.dynamic.deallocation, 12.5 class.free) is called to free the storage occupied by the object; the deallocation function is chosen as specified in 5.3.4 expr.new. If the object or array was allocated in a new-expression and the new-expression contains a new-placement, the storage occupied by the object is deallocated only if an appropriate placement operator delete is found, as specified in 5.3.4 expr.new.
with
If the object or array was allocated in a new-expression, the matching deallocation function (3.7.3.2 basic.stc.dynamic.deallocation, 5.3.4 expr.new, 12.5 class.free), if any, is called to free the storage occupied by the object.
5.7 expr.add paragraph 8 explicitly allows subtraction of two pointers to functions:
If two pointers point to the same object or function... and the two pointers are subtracted...However, 5.7 expr.add paragraph 2 requires that two pointers that are subtracted be pointers to an object type; function pointers are not allowed.
Being able to subtract two pointers to functions doesn't seem terribly useful, especially considering that subtracting two pointers to different functions appears to produce undefined behavior rather than simply a non-zero result, according to paragraph 6:
Unless both pointers point to elements of the same array object, or one past the last element of the array object, the behavior is undefined.
Proposed resolution (10/00):
Remove the words or function from paragraph 8.
From reflector messages core-7890, 7895, 7896, 7904, 8101-8106.
Nathan Myers: In 5.10 expr.eq , we have:
Pointers to objects or functions of the same type (after pointer conversions) can be compared for equality. Two pointers of the same type compare equal if and only if they are both null, both point to the same object or function, or both point one past the end of the same array.What does this say, when we have
int i[1];
int j[1];
about the expression (i+1 == j) ? It seems to require padding
between i[0] and j[0] so that the comparison will come
out false.
Mike Miller: I think this is reading more into the statement in 5.10 expr.eq paragraph 1 than is actually there. What does it mean for a pointer to "point to" an object? I can't find anything that definitively says that i+1 cannot "point to" j[0] (although it's obviously not required to do so). If i+1 is allowed to "point to" j[0], then i+1==j is allowed to be true, and there's no defect. There are places where aliasing is forbidden, but the N+1th element of an array doesn't appear to be one of them.
To put it another way, "points to" is undefined in the Standard. The only definition I can think of that encompasses the possible ways in which a pointer can get its value (e.g., the implementation-defined conversion of an arbitrary integer value to a pointer) is that it means "having the same value representation as would be produced by applying the (builtin) & operator to an lvalue expression designating that object". In other words, if the bits are right, it doesn't matter how you produced the value, as long as you didn't perform any operations that have undefined results. The expression i+1 is not undefined, so if the bits of i+1 are the same as those of &j[0], then i+1 "points to" j[0] and i+i==j is allowed to be true.
Tom MacDonald: C9X contains the following words for the "==" operator:
Two pointers compare equal if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.Matt Austern: I don't think there's anything wrong with saying that the result of
int x[1];
int y[1];
std::cout << (y == x + 1) << std::endl;
is implementation defined, or even that it's undefined.
Mike Miller: A similar question could be raised about different objects that (sequentially) share the same storage. Consider the following:
struct B {
virtual void f();
};
struct D1: B { };
struct D2: B { };
void g() {
B* bp1 = new D1;
B* bp2 = new (bp1) D2;
bp1 == bp2; // ???
}
Section
3.8
basic.life
paragraph 5 does
not list this kind of comparison among the pointer operations that cause
undefined behavior, so presumably the comparison is allowed. However,
5.10
expr.eq
paragraph 1 describes pointer comparison in terms of "[pointing] to the
same object," which bp1 and bp2 clearly do not do. How
should we describe the result of this comparison?
Jason Merrill: When you consider comparing pointers to void, this seems to suggest that no two objects can have the same address, depending on your interpretation of "point to the same object." This would cripple the empty base optimization.
3.9.2 basic.compound refers to 'pointers to void or objects or functions'. In that case, 5.10 expr.eq does not allow you to compare them; it only allows comparing pointers to objects and functions.
Proposed Resolution (10/00):
A valid value of an object pointer type represents either the address of a byte in memory (1.7 intro.memory) or a null pointer (4.10 conv.ptr). If an object of type T is located at an address A, a pointer of type cv T* whose value is the address A is said to point to that object, regardless of how the value was obtained. [Note: for instance, the address one past the end of an array (5.7 expr.add) would be considered to point to an unrelated object of the array's element type that might be located at that address.]
Two pointers of the same type compare equal if and only if they are both null, both point to the same function, or both represent the same address (3.9.2 basic.compound).
(See also paper J16/00-0011 = WG21 N1234.)
From reflector messages 8396-7.
Given
char arr[100];
sizeof(0,arr);
What does the sizeof expression return? According to 5.18 expr.comma paragraph 1, the comma operator yields an lvalue if the second argument is an lvalue. Since 4.2 conv.array paragraph 1 says that the array-to-pointer conversion yields an rvalue, it seems that sizeof should see an array type and give the answer 100. If so, the value of the sizeof expression would be different from that of the corresponding expression in C, but there is nothing in Annex C diff to indicate that an incompatible change was intended.
Proposed resolution (10/00):
Add the following as paragraph 3 of C.1.3 diff.expr:
5.16, 5.17, 5.18
Change: The result of a conditional expression, an assignment expression, or a comma expression may be an lvalue.
Rationale: C++ is an object-oriented language, placing relatively more emphasis on lvalues. For example, functions may return lvalues.
Effect on original feature: Change to semantics of well-defined feature. Some C expressions that implicitly rely on lvalue-to-rvalue conversions will yield different results. For example,char arr[100]; sizeof(0, arr)yields 100 in C++ and sizeof(char*) in C.
Difficulty of converting: Programs must add explicit casts to the appropriate rvalue.
How widely used: Rare.
From reflector messages core-7960, 7961, 7962 and 7965.
struct S {
static const int c = 5;
};
int a[S::c]; // error: S::c not in scope
Is this restriction intentional? If so, what was the rationale for the
restriction?
Bjarne Stroustrup: I think that once you have said S::, c is in scope so that
int a[S::c];
is ok.
Mike Miller: I'd like to think that's what it meant, but I don't believe that's what it said. According to 3.3 basic.scope paragraph 1, the scope of a name is the region "in which that name may be used as an unqualified name." You can, indeed, use a qualified name to refer to a name that is not in scope, but that only goes to reinforce my point that "S::c" is not in scope at the point where the expression containing it is used. I think the phrase "within its scope" is at best misleading and should be removed. (Unless there's a reason I'm missing for restricting the use of static member constants to their scope.)
As far as I can tell from 5.19 expr.const paragraph 2, "arithmetic constant expressions" (as distinct from "integral constant expressions") are used only in static initializers to distinguish between static and dynamic initialization. They include floating point types and exclude non-type template parameters, as well as the const variables and static data members.
There is a minor error in 5.19 expr.const paragraph 2. The first sentence says, "Other expressions are considered constant expressions only for the purpose of non-local static object initialization." However, 6.7 stmt.dcl paragraph 4 appears to rely on the same definition dealing with the initialization of local static objects. I think that the words "non-local" should be dropped and a cross reference to 6.7 stmt.dcl added.
I'm guessing that should be "non-static member," like the similar prohibition in 12.7 class.cdtor regarding out-of-lifetime access to members of non-POD class objects.
Proposed resolutions (10/00):
Remove the phrase "within its scope" in 9.4.2 class.static.data paragraph 4.
An arithmetic constant expression shall satisfy the requirements for an integral constant expression, except that
- floating literals need not be cast to integral or enumeration type, and
- conversions to floating point types are permitted.
This is not a defect; no change is required. The suggested wording would be more accurate, but since the effect on local initialization is unobservable the current wording is adequate.
Change the referenced sentence in 5.19 expr.const paragraph 4 to "An expression that designates the address of a subobject of a non-POD class object is not an address constant expression."
The wording of 6.4 stmt.select paragraph 1 is misleading. Instead of
The substatement in a selection-statement (both substatements, in the else form of the if statement) implicitly defines a local scope (3.3 basic.scope).
it should say
... each substatement, in the else form...
As is, one is left with the impression that both "then" and "else" clauses together form a single scope.
Proposed resolution (10/00): As suggested.
Mike Ball: I cannot find anything in the standard that tells me the meaning of a storage-class-specifier on a function template declaration. In particular, there is no indication what effect, if any, it has on the storage class of the instantiations.
There is an explicit prohibition of storage-class-specifiers on explicit specializations.
For example, if we have
template<class T> static int foo(T) { return sizeof(T); }
does this generate static functions for all instantiations? By
7.1.1
dcl.stc
the storage class applies to the name declared in the declarator, which
is the template foo, not an instantiation of foo, which
is named with a template-id. There is a statement in clause
14 that template names have linkage, which supports the contention that
"static" applies to the template, not to instantiations.
So what does the specifier mean? Lacking a direct statement in the standard, I see the following posibilities, in my preference order.
From John Spicer
The standard does say that a namespace scope template has external linkage unless it is a function template declared "static". It doesn't explicitly say that the linkage of the template is also the linkage of the instantiations, but I believe that is the intent. For example, a storage class is prohibited on an explicit specialization to ensure that a specialization cannot be given a different storage class than the template on which it is based.
Mike: This makes sense, but I couldn't find much support in the document. Sounds like yet another interpretation to add to the list.The standard does not talk about the linkage of instantiations, because only "names" are considered to have linkage, and instances are not really names. So, from an implementation point of view, instances have linkage, but from a language point of view, only the template from which the instances are generated has linkage.John: Agreed.
Mike: Which is why I think it would be cleaner to eliminate storage class specifiers entirely and rely on the unnamed namespace. There is a statement that specializations go into the namespace of the template. No big deal, it's not something it says, so we live with what's there."export" is an additional attribute that is separate from linkage, but that can only be applied to templates with external linkage.John: That would mean prohibiting static function templates. I doubt those are common, but I don't really see much motivation for getting rid of them at this point.
Mike: I can't find that restriction in the standard, though there is one that templates in an unnamed namespace can't be exported. I'm pretty sure that we intended it, though.John: I can't find it either. The "inline" case seems to be addressed, but not static. Surely this is an error as, by definition, a static template can't be used from elsewhere.
Proposed resolution (10/00):
Change the text in 14 temp paragraph 4 from:A template name may have linkage (3.5 basic.link).to:
A template name has linkage (3.5 basic.link). A non-member function template can have internal linkage; any other template name shall have external linkage. Entities generated from a template with internal linkage are distinct from all entities generated in other translation units.
Can a typedef redeclaration be done within a class?
class X {
typedef int I;
typedef int I;
};
See also
9.2
class.mem
,
Core issue 36,
and
Core issue 85.
Proposed Resolution (10/99): Change 7.1.3 dcl.typedef paragraph 2 from "In a given scope" to "In a given non-class scope."
The following code does not compile with the EDG compiler:
volatile const int a = 5;
int b[a];
The standard,
7.1.5.1
dcl.type.cv
, says:
A variable of const-qualified integral or enumeration type initialized by an integral constant expression can be used in integral constant expressions.This doesn't say it can't be const volatile-qualified, although I think that was what was intended.
Proposed Resolution (10/99): Change the referenced text in paragraph 2 of 7.1.5.1 dcl.type.cv to read:
I can't find the answer to the following in the standard. Does anybody have a reference?
The syntax for elaborated type specifier is
class foo<int> // foo is a template
On the other hand, a friend declaration seems to require this production,
An elaborated-type-specifier shall be used in a friend declaration for a class.*And in 14.5.3 temp.friend we find the example[Footnote: The class-key of the elaborated-type-specifier is required. —end footnote]
[Example:
template<class T> class task;
template<class T> task<T>* preempt(task<T>*);
template<class T> class task {
// ...
friend void next_time();
friend void process(task<T>*);
friend task<T>* preempt<T>(task<T>*);
template<class C> friend int func(C);
friend class task<int>;
template<class P> friend class frd;
// ...
};
Is there some special dispensation somewhere to allow the syntax in this
context? Is there something I've missed about elaborated-type-specifier?
Is it just another bug in the standard?
An additional problem was reported via comp.std.c++: the grammar does not allow the following example:
namespace A{
class B{};
};
namespace B{
class A{};
class C{
friend class ::A::B;
};
};
Proposed resolution (10/00):
Change the grammar in 7.1.5.3
dcl.type.elab to read
7.3 basic.namespace paragraph 2 says:
A name declared outside all named namespaces, blocks (6.3 stmt.block ) and classes (clause 9 class ) has global namespace scope (3.3.5 basic.scope.namespace ).But 3.3.5 basic.scope.namespace paragraph 3 says:
A name declared outside all named or unnamed namespaces (7.3 basic.namespace ), blocks (6.3 stmt.block ), function declarations (8.3.5 dcl.fct ), function definitions (8.4 dcl.fct.def ) and classes (clause 9 class ) has global namespace scope (also called global scope).7.3 basic.namespace should evidently be changed to match the wording in 3.3.5 basic.scope.namespace — the unnamed namespace is not global scope.
Proposed resolution (10/00):
Replace the first sentence of 3.3.5 basic.scope.namespace paragraph 3 with
The outermost declarative region of a translation unit is also a namespace, called the global namespace. A name declared in the global namespace has global namespace scope (also called global scope).
In the last sentence of the same paragraph, change "Names declared in the global namespace scope" to "Names with global namespace scope."
Replace 7.3 basic.namespace paragraph 2 with
The outermost declarative region of a translation unit is a namespace; see 3.3.5 basic.scope.namespace.
From reflector messages 8321-3.
John Spicer: I believe the standard is not clear with respect to this example:
namespace N {
template <class T> void f(T);
namespace M {
struct A {
friend void f<int>(int); // okay - refers to N::f
};
}
}
At issue is whether the friend declaration refers to N::f, or
whether it is invalid.
A note in 3.3.1 basic.scope.pdecl paragraph 6 says
friend declarations refer to functions or classes that are members of the nearest enclosing namespace ...I believe it is intended to mean unqualified friend declarations. Certainly friend void A::B() need not refer to a member of the nearest enclosing namespace. Only when the declarator is unqualified (i.e., it is a declaration and not a reference) does this rule need to apply. The presence of an explicit template argument list requires that a previous declaration be visible and renders this a reference and not a declaration that is subject to this rule.
Mike Miller: 7.3.1.2 namespace.memdef paragraph 3 says,
When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.On the other hand, the friend declaration would be a syntax error if f weren't declared as a template name; it would seem very strange not to find the declaration that made the friend declaration syntactically correct. However, it also seems strange to treat this case differently from ordinary functions and from templates:
namespace N {
template <class T> void f(T);
void g();
namespace M {
struct A {
friend void f<int>(int); // N::f
template <class T> friend void f(T); // M::f
friend void g(); // M::g
};
}
}
John Spicer: This section refers to "looking for a prior declaration". This gets back to an earlier discussion we've had about the difference between matching two declarations of the same name and doing name lookup. I would maintain that in f<int> the f is looked up using a normal lookup. In practice, this is really how it has to be done because the declaration could actually be f<int>::x.
Proposed resolution (10/00):
In 7.3.1.2 namespace.memdef paragraph 3, change
When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.to
When looking for a prior declaration of a class or a function declared as a friend, and when the name of the friend class or function is neither a qualified name nor a template-id, scopes outside the innermost enclosing namespace scope are not considered.Also, change the example in that paragraph as follows:
void h(int);
template <class T> void f2(T);
namespace A {
class X {
friend void f(X); // A::f(X) is a friend
friend void f2<>(int); // ::f2<>(int) is a friend
...
(See also issues 95, 136, 138, 139, 143, and 165.)
From reflector message core-7994:
Consider the following:
extern "C" void f();
namespace N {
extern "C" void f();
}
using N::f;
According to
7.3.3
namespace.udecl
paragraph 11, the using-declaration is an error:
If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, the program is ill-formed.Based on the context (7.3.3 namespace.udecl paragraph 10 simply reiterates the requirements of 3.3 basic.scope ), one might wonder if the failure to exempt extern "C" functions was intentional or an oversight. After all, there is only one function f() involved, because it's extern "C", so ambiguity is not a reason to prohibit the using-declaration.
This also breaks the relatively strong parallel between extern "C" functions and typedefs established in our discussion of Core issue 14 in Santa Cruz. There the question was for using-directives:
typedef unsigned int size_t;
extern "C" int f();
namespace N {
typedef unsigned int size_t;
extern "C" int f();
}
using namespace N;
int i = f(); // ambiguous "f"?
size_t x; // ambiguous "size_t"?
We decided for both that there was no ambiguity because each
pair of declarations declares the same entity. (According to
3
basic
paragraph 3, a typedef name is not an entity, but a type is; thus the
declarations of size_t declare the same entity "unsigned int".)
In the context of using-declarations, there is no explicit extension of the restrictions in 3.3 basic.scope paragraph 4 except as noted above for function declarations; thus the parallel scenario for a typedef is not ill-formed:
typedef unsigned int size_t;
namespace N {
typedef unsigned int size_t;
};
using N::size_t; // okay, both declarations
// refer to the same entity
I think the first sentence of
7.3.3
namespace.udecl
paragraph 11 ought to be rewritten as:
If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, and the declarations do not declare the same function, the program is ill-formed.
Proposed Resolution (10/99): As suggested.
From reflector messages core-8001 and core-8003.
Section 7.3.4 namespace.udir paragraph 3 uses the term extended-namespace-definition three times:
If a namespace is extended by an extended-namespace-definition after a using-directive for that namespace is given, the additional members of the extended namespace and the members of namespaces nominated by using-directives in the extended-namespace-definition can be used after the extended-namespace-definition.I think the intent is clear, but unfortunately I cannot find any other mention (or definition) of this term.
Mike Miller: True enough; in Section 7.3.1 namespace.def [the grammar] it's called an extension-namespace-definition.
Proposed Resolution (10/99): Systematically replace "extended-namespace-definition" by "extension-namespace-definition".
(Previously numbered 864.)
7.5 dcl.link paragraph 6 says the following:
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 7.5
dcl.link
paragraph 6 doesn't apply.
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.
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.
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.
For a linkage-specification without braces, i.e.
extern string-literal declaration
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.
[The following discussion is from messages 8722 and 8724.]
Bill Gibbons: In particular, these should be well-formed:
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++"
but these should not:
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)
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 7.5 dcl.link paragraph 4 that
the specified language linkage applies to the function types of all function declarators, function names, and variable names introduced by the declaration(s)
applies to both forms. I would thus expect that in
extern "C" void f(void(*)());
extern "C++" {
void f(void(*)());
}
extern "C++" f(void(*)());
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:
extern "C" void f(void (*fp1)());
extern "C++" {
void f(void(*fp2)());
}
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.
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?
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:
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
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 "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
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.
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.
Proposed resolution (04/01):
Change the first sentence of 7.5 dcl.link paragraph 1 from
All function types, function names, and variable names have a language linkage.
to
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).
to
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.
Add at the end of the final example on 7.5 dcl.link paragraph 4:
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.
}
Change 7.5 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:
extern "C" double f(); static double f(); // erroris ill-formed (7.1.1 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 (3.1 basic.def); the form of linkage-specification directly containing a single declaration is treated as an extern specifier (7.1.1 dcl.stc) for the purpose of determining whether the contained declaration is a definition. [Example:
extern "C" int i; // declaration extern "C" { int i; // definition }—end example] A linkage-specification directly containing a single declaration shall not specify a storage class. [Example:
extern "C" static void f(); // error—end example]
to
A declaration directly contained in a linkage-specification is treated as if it contains the extern specifier (7.1.1 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—end example]
From reflector message core-7714.
Consider the following:
extern "C" void foo()
{
extern void bar();
bar();
}
Does "bar()" have "C" language linkage?
The ARM is explicit and says
A linkage-specification for a function also applies to functions and objects declared within it.The DIS says
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).Is the body of a function definition part of the declaration?
From Mike Miller:
Yes: from 7 dcl.dcl paragraph 1,
From Dag Brück:
Consider the following where extern "C" has been moved to a separate declaration:
extern "C" void foo();
void foo() { extern void bar(); bar(); }
I think the ARM wording could possibly be interpreted such that bar() has
"C" linkage in my example, but not the DIS wording.
As a side note, I have always wanted to think that placing extern "C" on a function definition or a separate declaration would produce identical programs.
Proposed Resolution (04/01):
See the proposed resolution for Core issue 4, which covers this case.
The ODR should also be checked to see whether it addresses name and type linkage.
8.2 dcl.ambig.res paragraph 3 shows an example that includes <cstddef> with no using declarations or directives and refers to size_t without the std:: qualification.
Many references to size_t throughout the document omit the std:: namespace qualification.
This is a typical case. The use of std:: is inconsistent throughout the document.
In addition, the use of exception specifications should be examined for consistency.
(See also issue 282.)
Proposed resolution:
In 1.9 intro.execution paragraph 9, replace all two instances of "sig_atomic_t" by "std::sig_atomic_t".
In 3.1 basic.def paragraph 4, replace all three instances of "string" by "std::string" in the example and insert "#include <string>" at the beginning of the example code.
In 3.6.1 basic.start.main paragraph 4, replace
Calling the functionvoid exit(int);declared in <cstdlib>...
by
Calling the function std::exit(int) declared in <cstdlib>...
and also replace "exit" by "std::exit" in the last sentence of that paragraph.
In 3.6.1 basic.start.main first sentence of paragraph 5, replace "exit" by "std::exit".
In 3.6.2 basic.start.init paragraph 4, replace "terminate" by "std::terminate".
In 3.6.3 basic.start.term paragraph 1, replace "exit" by "std::exit" (see also issue 28).
In 3.6.3 basic.start.term paragraph 3, replace all three instances of "atexit" by "std::atexit" and both instances of "exit" by "std::exit" (see also issue 28).
In 3.6.3 basic.start.term paragraph 4, replace
Calling the functionvoid abort();declared in <cstdlib>...
by
Calling the function std::abort() declared in <cstdlib>...and "atexit" by "std::atexit" (see also issue 28).
In 3.7.3.1 basic.stc.dynamic.allocation paragraph 1 third sentence, replace "size_t" by "std::size_t".
In 3.7.3.1 basic.stc.dynamic.allocation paragraph 3, replace "new_handler" by "std::new_handler". Furthermore, replace "set_new_handler" by "std::set_new_handler" in the note.
In 3.7.3.1 basic.stc.dynamic.allocation paragraph 4, replace "type_info" by "std::type_info" in the note.
In 3.7.3.2 basic.stc.dynamic.deallocation paragraph 3, replace all four instances of "size_t" by "std::size_t".
In 3.8 basic.life paragraph 5, replace "malloc" by "std::malloc" in the example code and insert "#include <cstdlib>" at the beginning of the example code.
In 3.9 basic.types paragraph 2, replace "memcpy" by "std::memcpy" (the only instance in the footnote and both instances in the example) and replace "memmove" by "std::memmove" in the footnote (see also issue 43).
In 3.9 basic.types paragraph 3, replace "memcpy" by "std::memcpy", once in the normative text and once in the example (see also issue 43).
In 3.9.1 basic.fundamental paragraph 8 last sentence, replace "numeric_limits" by "std::numeric_limits".
In 5.2.7 expr.dynamic.cast paragraph 9 second sentence, replace "bad_cast" by "std::bad_cast".
In 5.2.8 expr.typeid paragraph 2, replace "type_info" by "std::type_info" and "bad_typeid" by "std::bad_typeid".
In 5.2.8 expr.typeid paragraph 3, replace "type_info" by "std::type_info".
In 5.2.8 expr.typeid paragraph 4, replace both instances of "type_info" by "std::type_info".
In 5.3.3 expr.sizeof paragraph 6, replace both instances of "size_t" by "std::size_t".
In 5.3.4 expr.new paragraph 11 last sentence, replace "size_t" by "std::size_t".
In 5.7 expr.add paragraph 6, replace both instances of "ptrdiff_t" by "std::ptrdiff_t".
In 5.7 expr.add paragraph 8, replace "ptrdiff_t" by "std::ptrdiff_t".
In 6.6 stmt.jump paragraph 2, replace "exit" by "std::exit" and "abort" by "std::abort" in the note.
In 8.2 dcl.ambig.res paragraph 3, replace "size_t" by "std::size_t" in the example.
In 8.4 dcl.fct.def paragraph 5, replace "printf" by "std::printf" in the note.
In 12.4 class.dtor paragraph 13, replace "size_t" by "std::size_t" in the example.
In 12.5 class.free paragraph 2, replace all four instances of "size_t" by "std::size_t" in the example.
In 12.5 class.free paragraph 6, replace both instances of "size_t" by "std::size_t" in the example.
In 12.5 class.free paragraph 7, replace all four instances of "size_t" by "std::size_t" in the two examples.
In 12.7 class.cdtor paragraph 4, replace "type_info" by "std::type_info".
In 13.6 over.built paragraph 13, replace all five instances of "ptrdiff_t" by "std::ptrdiff_t".
In 13.6 over.built paragraph 14, replace "ptrdiff_t" by "std::ptrdiff_t".
In 13.6 over.built paragraph 21, replace both instances of "ptrdiff_t" by "std::ptrdiff_t".
In 14.2 temp.names paragraph 4, replace both instances of "size_t" by "std::size_t" in the example. (The example is quoted in issue 96.)
In 14.3 temp.arg paragraph 1, replace "complex" by "std::complex", once in the example code and once in the comment.
In 14.7.3 temp.expl.spec paragraph 8, issue 24 has already corrected the example.
In 15.1 except.throw paragraph 6, replace "uncaught_exception" by "std::uncaught_exception".
In 15.1 except.throw paragraph 7, replace "terminate" by "std::terminate" and both instances of "unexpected" by "std::unexpected".
In 15.1 except.throw paragraph 8, replace "terminate" by "std::terminate".
In 15.2 except.ctor paragraph 3, replace "terminate" by "std::terminate".
In 15.3 except.handle paragraph 9, replace "terminate" by "std::terminate".
In 15.4 except.spec paragraph 8, replace "unexpected" by "std::unexpected".
In 15.4 except.spec paragraph 9, replace "unexpected" by "std::unexpected" and "terminate" by "std::terminate".
In 15.5 except.special paragraph 1, replace "terminate" by "std::terminate" and "unexpected" by "std::unexpected".
In the heading of 15.5.1 except.terminate, replace "terminate" by "std::terminate".
In 15.5.1 except.terminate paragraph 1, footnote in the first bullet, replace "terminate" by "std::terminate". In the same paragraph, fifth bullet, replace "atexit" by "std::atexit". In the same paragraph, last bullet, replace "unexpected_handler" by "std::unexpected_handler".
In 15.5.1 except.terminate paragraph 2, replace
In such cases,void terminate();is called...
by
In such cases, std::terminate() is called...
and replace all three instances of "terminate" by "std::terminate".
In the heading of 15.5.2 except.unexpected, replace "unexpected" by "std::unexpected".
In 15.5.2 except.unexpected paragraph 1, replace
...the functionvoid unexpected();is called...
by
...the function std::unexpected() is called....
In 15.5.2 except.unexpected paragraph 2, replace "unexpected" by "std::unexpected" and "terminate" by "std::terminate".
In 15.5.2 except.unexpected paragraph 3, replace "unexpected" by "std::unexpected".
In the heading of 15.5.3 except.uncaught, replace "uncaught_exception" by "std::uncaught_exception".
In 15.5.3 except.uncaught paragraph 1, replace
The functionbool uncaught_exception()returns true...
by
The function std::uncaught_exception() returns true....
In the last sentence of the same paragraph, replace "uncaught_exception" by "std::uncaught_exception".
(From J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")
There are two sub-issues. The first concerns the statement in 8.3 dcl.meaning paragraph 1,
The id-expression of a declarator-id shall be a simple identifier except for the declaration of some special functions (12.3 class.conv , 12.4 class.dtor , 13.5 over.oper ) and for the declaration of template specializations or partial specializations (14.7 temp.spec ).The second sub-issue is regarding another statement in the same paragraph:
A declarator-id shall not be qualified except for the definition of a member function (9.3 class.mfct ) or static data member (9.4 class.static ) or nested class (9.7 class.nest ) outside of its class, the definition or explicit instantiation of a function, variable or class member of a namespace outside of its namespace, or...Analysis
The problem in the first sub-issue is that the wrong syntactic non-terminal is mentioned. The relevant portions of the grammar are:
If an unqualified-id is used as the id-expression of a declarator-id, it shall be a simple identifier except...However, it does not appear that this restriction has any meaning; all of the possible cases of unqualified-ids are represented in the list of exceptions! Rather than recasting the sentence into a correct but useless form, it would be better to remove it altogether.
The second sub-issue deals with the conditions under which a qualified-id can be used in a declarator, including "the definition of a...nested class" and "the definition or explicit instantiation of a...class member of a namespace." However, the name in a class definition is not part of a declarator; these constructs do not belong in a list of declarator contexts.
Proposed Resolution for sub-issue 1 (04/99):
The suggested resolution for the first sub-issue overlooked the fact that the existing wording has the additional effect of prohibiting the use of the non-identifier syntax for declaring other than the listed entities. Thus the proposed wording for the first sub-issue is:
Change 8.3 dcl.meaning paragraph 1 from:
The id-expression of a declarator-id shall be a simple identifier except...to:
An unqualified-id occurring in a declarator-id shall be a simple identifier except...
Proposed Resolution for sub-issue 2 (10/99):
Change 8.3 dcl.meaning paragraph 1 from:
A declarator-id shall not be qualified except for the definition of a member function (9.3 class