Document number: | J16/01-0057 = WG21 N1343 |
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 for which the Committee (J16 + WG21) has decided that no action is required, that is, issues with status "NAD" ("Not A Defect"), "dup" (duplicate), and "extension."
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.
In 3.2 basic.def.odr paragraph 4 bullet 4, it's presumably the case that a conversion to T* requires that T be complete only if the conversion is from a different type. One could argue that there is no conversion (and therefore the text is accurate as it stands) if a cast does not change the type of the expression, but it's probably better to be more explicit here.
On the other hand, this text is non-normative (it's in a note).
Rationale (04/99): The relevant normative text makes this clear. Implicit conversion and static_cast are defined (in 4 conv and 5.2.9 expr.static.cast , respectively) as equivalent to declaration with initialization, which permits pointers to incomplete types, and dynamic_cast (5.2.7 expr.dynamic.cast ) explicitly prohibits pointers to incomplete types.
From reflector message core-7848.
Consider this code:
struct Base { enum { a, b, c, next }; }; struct Derived : public Base { enum { d = Base::next, e, f, next }; };The idea is that the enumerator "next" in each class is the next available value for enumerators in further derived classes.
If we had written
enum { d = next, e, f, next };I think we would run afoul of 3.3.6 basic.scope.class :
A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.But in the original code, we don't have an unqualified "next" that refers to anything but the current scope. I think the intent was to allow the code, but I don't find the wording clear on on that point.
Is there another section that makes it clear whether the original code is valid? Or am I being obtuse? Or should the quoted section say "An unqualified name N used in a class ..."?
Rationale (04/99): It is sufficiently clear that "name" includes qualified names and hence the usual lookup rules make this legal.
From reflector message core-7954.
When a union is used in argument-dependent lookup, the union's type is not an associated class type. Consequently, code like this will fail to work.
union U { friend void f(U); }; int main() { U u; f(u); // error: no matching f — U is not an associated class }Is this an error in the description of unions in argument-dependent lookup?
Also, this section is written as if unions were distinct from classes. So adding unions to the "associated classes" requires either rewriting the section so that "associated classes" can include unions, or changing the term to be more inclusive, e.g. "associated classes and unions" or "associated types".
Jason Merrill: Perhaps in both cases, the standard text was intended to only apply to anonymous unions.
Liam Fitzpatrick: One cannot create expressions of an anonymous union type.
Rationale (04/99): Unions are class types, so the example is well-formed. Although the wording here could be improved, it does not rise to the level of a defect in the Standard.
From reflector message 8155.
3.5 basic.link paragraph 8 says,
A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.2 basic.scope.local )) shall not be used to declare an entity with linkage.This wording does not, but should, prohibit use of an unnamed local type in the declaration of an entity with linkage. For example,
void f() { extern struct { } x; // currently allowed }
Proposed resolution: Change the text in 3.5 basic.link paragraph 8 from:
A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.2 basic.scope.local)) shall not be used to declare an entity with linkage.to:
A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.2 basic.scope.local)) or an unnamed type shall not be used to declare an entity with linkage.In section 3.5 basic.link paragraph 8, add to the example, before the closing brace of function f:
extern struct {} x; // ill-formed
Rationale (10/00): The proposed change would have introduced an incompatibility with the C language. For example, the global declaration
static enum { A, B, C } abc;
represents an idiom that is used in C but would be prohibited under this resolution.
According to 3.2 basic.def.odr paragraph 5, it is possible for a static data member of a class template to be defined more than once in a given program provided that each such definition occurs in a different translation unit and the ODR is met.
Now consider the following example:
src1.cpp:
#include <iostream> int initializer() { static int counter; return counter++; } int g_data1 = initializer(); template<class T> struct exp { static int m_data; }; template<class T> int exp<T>::m_data = initializer(); int g_data2 = initializer(); extern int g_data3; int main() { std::cout << exp<char>::m_data << ", " << g_data1 << ", " << g_data2 << ", " << g_data3 << std::endl; return 0; }
src2.cpp:
extern int initializer(); int g_data3 = initializer(); template<class T> struct exp { static int m_data; }; template<class T> int exp<T>::m_data = initializer(); void func() { exp<char>::m_data++; }
The specialization exp<char>::m_data is implicitly instaniated in both translation units, hence (14.7.1 temp.inst paragraph 1) its initialization occurs. And for both definitions of exp<T>::m_data the ODR is met. According to 3.6.2 basic.start.init paragraph 1:
Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.
But for exp<T>::m_data we have two definitions. Does it mean that both g_data1 and g_data3 are guaranteed to be dynamically initialized before exp<char>::m_data?
Suggested Resolution: Insert the following sentence before the last two sentences of 3.2 basic.def.odr paragraph 5:
In the case of D being a static data member of a class template the following shall also hold:
- for a given (not explicit) specialization of D initialized dynamically (3.6.2 basic.start.init), the accumulated set of objects initialized dynamically in namespace scope before the specialization of D shall be the same in every translation unit that contains the definition for this specialization.
Notes from 10/01 meeting:
It was decided that this issue is not linked to issue 270 and that there is no problem, because there is only one instantiation (see 2.1 lex.phases paragraph 8).
From reflector message 8653.
The default global operators delete are specified to not throw, but there is no requirement that replacement global, or class-specific, operators delete must not throw. That ought to be required.
In particular:
We already require that all versions of an allocator's deallocate() must not throw, so that part is okay.
Rationale (04/00):
3.8 basic.life and 12.4 class.dtor discuss explicit management of object lifetime. It seems clear that most object lifetime issues apply to sub-objects (array elements, and data members) as well. The standard supports
struct X { T t } x; T* pt = &x.t; pt->~T(); new(pt) T;
and this kind of behavior is useful in allocators.
However the standard does not seem to prohibit the same operations on base sub-objects.
struct D: B{ ... } d; B* pb = &d; pb->~B(); new(pb) B;
However if B and/or D have virtual member functions or virtual bases, it is unlikely that this code will result in a well-formed D object in current implementations (note that the various lines may be in different functions).
Suggested resolution: 12.4 class.dtor should be modified so that explicit destruction of base-class sub-objects be made illegal, or legal only under some restrictive conditions.
Rationale (04/01):
Reallocation of a base class subobject is already disallowed by 3.8 basic.life paragraph 7.
Paragraph 3 of section 4.5 conv.prom contains a statement saying that if a bit-field is larger than int or unsigned int, no integral promotions apply to it. This phrase needs further clarification, as it is hardly possible to fugure out what it means. See below.
Assuming a machine with a size of general-purpose register equal 32 bits (where a byte takes up 8 bits) and a C++ implementation where an int is 32 bits and a long is 64 bits. And the following snippet of code:
struct ExternalInterface { long field1:36, field2:28; }; int main() { ExternalInterface myinstance = { 0x100000001L, 0x12,}; if(myinstance.field1 < 0x100000002L) { //do something } }
Does the standard prohibit the implementation from promoting field1's value into two general purpose registers? And imposes a burden of using shift machine instructions to work with the field's value? What else could that phrase mean?
Either alternative is implementation specific, so I don't understand why the phrase "If the bit-field is larger yet, no integral promotions apply to it" made it to the standard.
Notes from 10/01 meeting:
The standard of course does not dictate what an implementation might do with regard to use of registers or shift instructions in the generated code. The phrase cited means only that a larger bit-field does not undergo integral promotions, and therefore it retains the type with which it was declared (long in the above example). The Core Working Group judged that this was sufficiently clear in the standard.
Note that 9.6 class.bit paragraph 1 indicates that any bits in excess of the size of the underlying type are padding bits and do not participate in the value representation. Therefore the field1 bit field in the above example is not capable of holding the indicated values, which require more than 32 bits.
From reflector message core-7865.
An operator expression can, according to 5 expr paragraph 2, require transformation into function call syntax. The reference in that paragraph is to 13.5 over.oper , but it should be to 13.3.1.2 over.match.oper .
Rationale (04/99): The subsections 13.5.1 over.unary , 13.5.2 over.binary , etc. of the referenced section are in fact relevant.
Is it okay to use a static_cast to cast from a private base class to a derived class? That depends on what the words "valid standard conversion" in paragraph 8 mean — do they mean the conversion exists, or that it would not get an error if it were done? I think the former was intended — and therefore a static_cast from a private base to a derived class would be allowed.
Rationale (04/99): A static_cast from a private base to a derived class is not allowed outside a member from the derived class, because 4.10 conv.ptr paragraph 3 implies that the conversion is not valid. (Classic style casts work.)
Is it okay for a static_cast to drop exception specifications?
void f() throw(int); int main () { static_cast<void (*)() throw()>(f); // Okay? void (*p)() throw() = f; // Error }
The fact that a static_cast is defined, more or less, as an initialization suggests that a check ought to be made.
One tricky point: this is another case where the general rule that the reverse of an implicit cast is allowed as a static_cast bites you -- the reverse conversion doesn't drop exception specifications, and so is okay. Perhaps this should be treated like casting away constness.
Mike Miller comments in message 9306 : I don't think that case can arise. According to 15.4 except.spec,
An exception-specification shall appear only on a function declarator in a function, pointer, reference, or pointer to member declaration or definition.
We strengthened that in issue 87 (voted to DR status in Copenhagen) to
An exception-specification shall appear only on a function declarator for a function type, pointer to function type, reference to function type, or pointer to member function type that is the top-level type of a declaration or definition, or on such a type appearing as a parameter or return type in a function declarator.
As I read that, you can't put an exception-specification on the type-id in a static_cast, which means that a static_cast can only weaken, not strengthen, the exception specification.
The core WG discussed this at the 10/01 meeting and agreed.
From reflector message core-7761.
Section 12.5 class.free paragraph 4 says:
If a delete-expression begins with a unary :: operator, the deallocation function's name is looked up in global scope. Otherwise, if the delete-expression is used to deallocate a class object whose static type has a virtual destructor, the deallocation function is the one found by the lookup in the definition of the dynamic type's virtual destructor (12.4 class.dtor ). Otherwise, if the delete-expression is used to deallocate an object of class T or array thereof, the static and dynamic types of the object shall be identical and the deallocation function's name is looked up in the scope of T. If this lookup fails to find the name, the name is looked up in the global scope. If the result of the lookup is ambiguous or inaccessible, or if the lookup selects a placement deallocation function, the program is ill-formed.I contrast that with 5.3.4 expr.new paragraphs 16 and 17:
If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function (12.5 class.free ), and the constructor (12.1 class.ctor ). If the new-expression creates an array of objects of class type, access and ambiguity control are done for the destructor (12.4 class.dtor ).I think nothing in the latter paragraphs implies that the deallocation function found is the same as that for a corresponding delete-expression. I suspect that may not have been intended and that the lookup should occur "as if for a delete-expression".If any part of the object initialization described above terminates by throwing an exception and a suitable deallocation function can be found, the deallocation function is called to free the memory in which the object was being constructed, after which the exception continues to propagate in the context of the new-expression. If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed. [Note: This is appropriate when the called allocation function does not allocate memory; otherwise, it is likely to result in a memory leak. ]
Rationale:
Paragraphs 16 through 18 are sufficiently correct and unambiguous as written.
From reflector messages 8132-3, 8136-7, 8141, 8148, 8150-51.
Clause 5 expr paragraph 4 appears to grant an implementation the right to generate code for a function call like
f(new T1, new T2)in the order
Suggested resolution: either forbid the ordering above or expand the requirement for reclaiming storage to include exceptions thrown in all operations between the allocation and the completion of the constructor.
Rationale (10/99): Even in the "traditional" ordering of the calls to allocation functions and constructors, memory can still leak. For instance, if T1 were successfully constructed and then the construction of T2 were terminated by an exception, the memory for T1 would be lost. Programmers concerned about memory leaks will avoid this kind of construct, so it seems unnecessary to provide special treatment for it to avoid the memory leaks associated with one particular implementation strategy.
From messages 8923-30, 8938.
The size requested by an array allocation is computed by multiplying the number of elements requested by the size of each element and adding an implementation-specific amount for overhead. It is possible for this calculation to overflow. Is an implementation required to detect this situation and, for instance, throw std::bad_alloc?
On one hand, the maximum allocation size is one of the implementation limits specifically mentioned in Annex B limits, and, according to 1.4 intro.compliance paragraph 2, an implementation is only required to "accept and correctly execute" programs that do not violate its resource limits.
On the other hand, it is difficult or impossible for user code to detect such overflows in a portable fashion, especially given that the array allocation overhead is not fixed, and it would be a service to the user to handle this situation gracefully.
Rationale (04/01):
Each implementation is required to document the maximum size of an object (Annex B limits). It is not difficult for a program to check array allocations to ensure that they are smaller than this quantity. Implementations can provide a mechanism in which users concerned with this problem can request extra checking before array allocations, just as some implementations provide checking for array index and pointer validity. However, it would not be appropriate to require this overhead for every array allocation in every program.
An expression of the form pointer + enum (see paragraph 5) is not given meaning, and ought to be, given that paragraph 2 of this section makes it valid. Presumably, the enum value should be converted to an integral value, and the rest of the processing done on that basis. Perhaps we want to invoke the integral promotions here.
[Should this apply to (pointer - enum) too?]
Rationale (04/99): Paragraph 1 invokes "the usual arithmetic conversions" for operands of enumeration type.
(It was later pointed out that the builtin operator T* operator+(T*, ptrdiff_t) (13.6 over.built paragraph 13) is selected by overload resolution. Consequently, according to 13.3.1.2 over.match.oper paragraph 7, the operand of enumeration type is converted to ptrdiff_t before being interpreted according to the rules in 5.7 expr.add .)
From reflector message core-7982.
Consider:
int* p = false; // Well-formed? int* q = !1; // What about this?>From 3.9.1 basic.fundamental paragraph 6: "As described below, bool values behave as integral types."
From 4.10 conv.ptr paragraph 1: "A null pointer constant is an integral constant expression rvalue of integer type that evaluates to zero."
From 5.19 expr.const paragraph 1: "An integral constant-expression can involve only literals, enumerators, const variables or static members of integral or enumeration types initialized with constant expressions, ..."
In 2.13.1 lex.icon : No mention of true or false as an integer literal.
From 2.13.5 lex.bool : true and false are Boolean literals.
So the definition of q is certainly valid, but the validity of p depends on how the sentence in 5.19 expr.const is parsed. Does it mean
If the latter, then (3.0 < 4.0) is a constant expression, which I don't think we ever wanted. If the former, though, we have the anomalous notion that true and false are not constant expressions.
Now, you may argue that you shouldn't be allowed to convert false to a pointer. But what about this?
static const bool debugging = false; // ... int table[debugging? n+1: n];Whether the definition of table is well-formed hinges on whether false is an integral constant expression.
I think that it should be, and that failure to make it so was just an oversight.
Rationale (04/99): A careful reading of 5.19 expr.const indicates that all types of literals can appear in integral constant expressions, but floating-point literals must immediately be cast to an integral type.
From reflector messages 8243-4.
9.5 class.union paragraph 3 implies that anonymous unions in unnamed namespaces need not be declared static (it only places that restriction on anonymous unions "declared in a named namespace or in the global namespace").
However, 7.1.1 dcl.stc paragraph 1 says that "global anonymous unions... shall be declared static." This could be read as prohibiting anonymous unions in unnamed namespaces, which are the preferred alternative to the deprecated use of static.
Rationale (10/99): An anonymous union in an unnamed namespace is not "a global anonymous union," i.e., it is not a member of the global namespace.
From reflector messages core-7966 through 7974.
A change was introduced into the language that made names first declared in friend declarations "invisible" to normal lookups until such time that the identifier was declared using a non-friend declaration. This is described in 7.3.1.2 namespace.memdef paragraph 3 and 11.4 class.friend paragraph 9 (and perhaps other places).
The standard gives examples of how this all works with friend declarations, but there are some cases with nonfriend elaborated type specifiers for which there are no examples, and which might yield surprising results.
The problem is that an elaborated type specifier is sometimes a declaration and sometimes a reference. The meaning of the following code changes depending on whether or not friend class names are injected (visibly) into the enclosing namespace scope.
struct A; struct B; namespace N { class X { friend struct A; friend struct B; }; struct A *p; // N::A with friend injection, ::A without struct B; // always N::B }Is this the desired behavior, or should all elaborated type specifiers (and not just those of the form "class-key identifier;") have the effect of finding previously declared "invisible" names and making them visible?
Mike Miller: That's not how I would categorize the effect of "struct B;". That declaration introduces the name "B" into namespace N in exactly the same fashion as if the friend declaration did not exist. The preceding friend declaration simply stated that, if a class N::B were ever defined, it would have friendly access to the members of N::X. In other words, the lookups in both "struct A*..." and "struct B;" ignore the friend declarations.
(The standard is schizophrenic on the issue of whether such friend declarations introduce names into the enclosing namespace. 3.3 basic.scope paragraph 4 says,
John Spicer: The previous declaration of B is not completely ignored though, because certainly changing "friend struct B;" to "friend union B;" would result in an error when B was later redeclared as a struct, wouldn't it?
Bill Gibbons: Right. I think the intent was to model this after the existing rule for local declarations of functions (which dates back to C), where the declaration is introduced into the enclosing scope but the name is not. Getting this right requires being somewhat more rigorous about things like the ODR because there may be declaration clashes even when there are no name clashes. I suspect that the standard gets this right in most places but I would expect there to be a few that are still wrong, in addition to the one Mike pointed out.
Mike Miller: Regarding would result in an error when B was later redeclared
I don't see any reason why it should. The restriction that the class-key must agree is found in 7.1.5.3 dcl.type.elab and is predicated on having found a matching declaration in a lookup according to 3.4.4 basic.lookup.elab . Since a lookup of a name declared only (up to that point) in a friend declaration does not find that name (regardless of whether you subscribe to the "does-not-introduce" or "introduces-invisibly" school of thought), there can't possibly be a mismatch.
I don't think that the Standard's necessarily broken here. There is no requirement that a class declared in a friend declaration ever be defined. Explicitly putting an incompatible declaration into the namespace where that friend class would have been defined is, to me, just making it impossible to define — which is no problem, since it didn't have to be defined anyway. The only error would occur if the same-named but unbefriended class attempted to use the nonexisting grant of friendship, which would result in an access violation.
(BTW, I couldn't find anything in the Standard that forbids defining a class with a mismatched class-key, only using one in an elaborated-type-specifier. Is this a hole that needs to be filled?)
John Spicer: This is what 7.1.5.3 dcl.type.elab paragraph 3 says:
class B; union B {};and
union B {}; class B;are both invalid. I think this paragraph is intended to say that. I'm not so sure it actually does say that, though.
Mike Miller: Regarding I think the intent was to model this after the existing rule for local declarations of functions (which dates back to C)
Actually, that's not the C (1989) rule. To quote the Rationale from X3.159-1989:
Regarding Getting this right requires being somewhat more rigorous
Yes, I think if this is to be made illegal, it would have to be done with the ODR; the name-lookup-based current rules clearly (IMHO) don't apply. (Although to be fair, the [non-normative] note in 3.3 basic.scope paragraph 4 sounds as if it expects friend invisible injection to trigger the multiple-declaration provisions of that paragraph; it's just that there's no normative text implementing that expectation.)
Bill Gibbons: Nor does the ODR currently disallow:
translation unit #1 struct A; translation unit #2 union A;since it only refers to class definitions, not declarations.
But the obvious form of the missing rule (all declarations of a class within a program must have compatible struct/class/union keys) would also answer the original question.
The declarations need not be visible. For example:
translation unit #1 int f() { return 0; } translation unit #2: void g() { extern long f(); }is ill-formed even though the second "f" is not a visible declaration.
Rationale (10/99): The main issue (differing behavior of standalone and embedded elaborated-type-specifiers) is as the Committee intended. The remaining questions mentioned in the discussion may be addressed in dealing with related issues.
(See also issues 136, 138, 139, 143, 165, and 166.)
7.3.1.2 namespace.memdef paragraph 2 says,
Members of a named namespace can also be defined outside that namespace by explicit qualification (3.4.3.2 namespace.qual ) of the name being defined, provided that the entity being defined was already declared in the namespace...It is not clear whether block-scope extern declarations and friend declarations are sufficient to permit the named entities to be defined outside their namespace. For example,
namespace NS { struct A { friend struct B; }; void foo() { extern void bar(); } } struct NS::B { }; // 1) legal? void NS::bar() { } // 2) legal?
Rationale (10/99): Entities whose names are "invisibly injected" into a namespace as a result of friend declarations are not "declared" in that namespace until an explicit declaration of the entity appears at namespace scope. Consequently, the definitions in the example are ill-formed.
(See also issues 95, 136, 138, 139, 143, and 166.)
7.3.3 namespace.udecl paragraph says,
A using-declaration shall not name a template-id.It is not clear whether this prohibition applies to the entity for which the using-declaration is a synonym or to any name that appears in the using-declaration. For example, is the following code well-formed?
template <typename T> struct base { void bar (); }; struct der : base<int> { using base<int>::bar; // ill-formed ? };
Rationale (10/99): 7.3.3 namespace.udecl paragraph 1 says, "A using-declaration introduces a name..." It is the name that is thus introduced that cannot be a template-id.
(Previously numbered 922.)
Issue 1
7.5 dcl.link paragraph 6 says the following:
extern "C" int f(void); namespace A { extern "C" int f(void); }; using namespace A; int i = f(); // Ok because only one function f() or // ill-formedFor name lookup, both declarations of f are visible and overloading cannot distinguish between them. Has the compiler to check that these functions are really the same function or is the program in error?
Rationale: These are the same function for all purposes.
Issue 2
A similar question may arise with typedefs:
// vendor A typedef unsigned int size_t; // vendor B namespace std { typedef unsigned int size_t; } using namespace std; size_t something(); // error?Is this valid because the typedef size_t refers to the same type in both namespaces?
Rationale (04/99): In 7.3.4 namespace.udir paragraph 4:
If name lookup finds a declaration for a name in two different namespaces, and the declarations do not declare the same entity and do not declare functions, the use of the name is ill-formed.The term entity applied to typedefs refers to the underlying type or class (3 basic , paragraph 3); therefore both declarations of size_t declare the same entity and the above example is well-formed.
From reflector messages 8020-21, 8290-8304, 8306-7.
Steve Clamage: I can't find anything in the standard that prohibits a language linkage on an operator function. For example:
extern "C" int operator+(MyInt, MyInt) { ... }
Clearly it is a bad idea, you could have only one operator+ with "C" linkage in the entire program, and you can't call the function from C code.
Mike Miller: Well, you can't name an operator function in C code, but if the arguments are compatible (e.g., not references), you can call it from C code via a pointer. In fact, because the language linkage is part of the function type, you couldn't pass the address of an operator function into C code unless you could declare the function to be extern "C".
Fergus Henderson: In the general case, for linkage to languages other than C, this could well make perfect sense.
Steve Clamage:
But is it disallowed (as opposed to being stupid), and if so, where in the standard does it say so?
Mike Miller: I don't believe there's a restriction. Whether that is because of the (rather feeble) justification of being able to call an operator from C code via a pointer, or whether it was simply overlooked, I don't know.
Fergus Henderson: I don't think it is disallowed. I also don't think there is any need to explicitly disallow it.
Steve Clamage: I don't think the standard is clear enough on this point. I'd like to see a clarification.
I think either of these two clarifications would be appropriate:
extern "C" T operator+(T,T); // ok extern "C" T operator-(T,T); // ok extern "C" U operator-(U); // error, two extern "C" operator-
Mike Miller: I think the point here is that something like
extern "xyzzy" bool operator<(S&,S&)could well make sense, if language xyzzy is sufficiently compatible with C++, and the one-function rule only applies to extern "C", not to other language linkages. Given that it might make sense to have general language linkages for operators, is it worthwhile to make an exception to the general rule by saying that you can have any language linkage on an operator function except "C" linkage? I don't like exceptions to general rules unless they're very well motivated, and I don't see sufficient motivation to make one here.
Certainly this capability isn't very useful. There are lots of things in C++ that aren't very useful but just weren't worth special-casing out of the language. I think this falls into the same category.
Mike Ball: I DON'T want to forbid operator functions within an extern "C". Rather I want to add operator functions to that sentence in paragraph 4 of 7.5 dcl.link which reads
A C language linkage is ignored for the names of class members and the member function type of class member functions.My reason is simple: C linkage makes a total hash of scope. Any "C" functions declared with the same name in any namespace scope are the same function. In other words, namespaces are totally ignored.
This provision was added in toward the end of the standardization process, and was, I thought, primarily to make it possible to put the C library in namespace std. Otherwise, it seems an unwarrented attack on the very concept of scope. We (wisely) didn't force this on static member functions, since it would essentially promote them to the global scope.
Now I think that programmers think of operator functions as essentially part of a class. At least for one very common design pattern they are treated as part of the class interface. This pattern is the reason we invented Koenig lookup for operator functions.
What happens when such a class definition is included, deliberately or not, in an extern "C" declaration? The member operators continue to work, but the non-member operators can suddenly get strange and hard to understand messages. Quite possibly, they get the messages only when combined with other classes in other compilation units. You can argue that the programmer shouldn't put the class header in a linkage delaration in the first place, but I can still find books that recommend putting `extern "C"' around entire header files, so it's going to happen.
I think that including operator functions in the general exclusion from extern "C" doesn't remove a capability, rather it ensurs a capability that programmers already think they have.
Rationale (10/00):
The benefits of creating an exception for operator functions were outweighed by the complexity of adding another special case to the rules.
7.5 dcl.link paragraph 4 says,
A C language linkage is ignored for the names of class members and the member function types of class member functions.This makes good sense, since C linkage names typically aren't compatible with the naming used for member functions at link time, nor is C language linkage function type necessarily compatible with the calling convention for passing this to a non-static member function.
But C language linkage type (not name) for a static member function is invaluable for a common programming idiom. When calling a C function that takes a pointer to a function, it's common to use a private static member function as a "trampoline" which retrieves an object reference (perhaps by casting) and then calls a non-static private member function. If a static member function can't have a type with C language linkage, then a global or friend function must be used instead. These alternatives expose more of a class's implementation than a static member function; either the friend function itself is visible at namespace scope alongside the class definition or the private member function must be made public so it can be called by a non-friend function.
Suggested Resolution: Change the sentence cited above to:
A C language linkage is ignored for the names of class members and the member function types of non-static class member functions.The example need not be changed because it doesn't involve a static member function.
The following workaround accomplishes the goal of not exposing the class's implementation, but at the cost of significant superstructure and obfuscation:
// foo.h extern "C" typedef int c_func(int); typedef int cpp_func(int); class foo { private: c_func* GetCallback(); static int Callback(int); }; // foo.cpp #include "foo.h" // A local pointer to the static member that will handle the callback. static cpp_func* cpp_callback=0; // The C function that will actually get registered. extern "C" int CFunk(int i) { return cpp_callback(i); } c_func* foo::GetCallback() { cpp_callback = &Callback; // Only needs to be done once. return &CFunk; }
Rationale (10/99): The Standard correctly reflects the intent of the Committee.
(Previously numbered 929.)
8.3.5 dcl.fct paragraph 2 says:
If the parameter-declaration-clause is empty, the function takes no arguments. The parameter list (void) is equivalent to the empty parameter list.Can a typedef to void be used instead of the type void in the parameter list?
Rationale: The IS is already clear that this is not allowed.
Paragraph 9 of says that extra default arguments added after a using-declaration but before a call are usable in the call, while 7.3.3 namespace.udecl paragraph 9 says that extra function overloads are not. This seems inconsistent, especially given the similarity of default arguments and overloads.
Rationale (10/99): The Standard accurately reflects the intent of the Committee.
(Previously numbered 888.)
class Foo { public: Foo() {} ~Foo() {} }; class A : virtual private Foo { public: A() {} ~A() {} }; class Bar : public A { public: Bar() {} ~Bar() {} };~Bar() calls ~Foo(), which is ill-formed due to access violation, right? (Bar's constructor has the same problem since it needs to call Foo's constructor.) There seems to be some disagreement among compilers. Sun, IBM and g++ reject the testcase, EDG and HP accept it. Perhaps this case should be clarified by a note in the draft.
In short, it looks like a class with a virtual private base can't be derived from.
Rationale: This is what was intended.
(Previously numbered 925.)
Footnote 98 says:
As specified previously in clause 11 class.access , private members of a base class remain inaccessible even to derived classes unless friend declarations within the base class declaration are used to grant access explicitly.This footnote does not fit with the algorithm provided in 11.2 class.access.base paragraph 4 because it does not take into account the naming class concept introduced in this paragraph.
(See also paper J16/99-0002 = WG21 N1179.)
Rationale (10/99): The footnote should be read as referring to immediately-derived classes, and is accurate in that context.
11.4 class.friend, paragraph 7, says
A name nominated by a friend declaration shall be accessible in the scope of the class containing the friend declaration.
Does that mean the following should be illegal?
class A { void f(); }; class B { friend void A::f(); }; // Error: A::f not accessible from B
I discussed this with Bjarne in email, and he thinks it was an editorial error and this was not the committee's intention. The paragraph seems to have been added in the pre-Kona (24 Sept 1996) mailing, and I could not find anything in the previous meeting's (Stockholm) mailings which led me to believe this was intentional. The only compiler vendor which I think currently implements it is the latest release (2.43) of the EDG front end.
Proposed resolution (10/00):
Remove the first sentence of 11.4 class.friend, paragraph 7.
Rationale (04/01):
After the 10/00 vote to accept this issue as a DR with the proposed resolution, it was noted that the first two sentences of 11 class.access paragraph 3 cause the proposed change to have no effect:
Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions. [Note: access control applies to names nominated by friend declarations (11.4 class.friend) and using-declarations (7.3.3 namespace.udecl). ]
In addition to the obvious editing to the text of the note, an exception to the blanket statement in the first sentence would also be required. However, discussion during the 04/01 meeting failed to produce consensus on exactly which names in the friend declaration should be exempted from the requirements of access control.
One possibility would be that only the name nominated as friend should be exempt. However, that approach would make it impossible to name a function as a friend if it used private types in its parameters or return type. Another suggestion was to ignore access for every name used in a friend declaration. That approach raised a question about access within the body of a friend function defined inline in the class body — the body is part of the declaration of a function, but references within the body of a friend function should still be subject to the usual access controls.
Other possibilities were raised, such as allowing the declaration of a friend member function if the declaration were permissible in its containing class, or taking the union of the access within the befriending class and the befriended entity. However, these suggestions would have been complex and difficult to specify correctly.
Ultimately it was decided that the original perceived defect was not sufficiently serious as to warrant the degree of complexity required to resolve it satisfactorily and the issue was consequently declared not to be a defect. It was observed that most of the problems involved with the current state of affairs result from inability to declare a particular member function as a friend; in such cases, an easy workaround is simply to befriend the entire class rather than the specific member function.
(Previously numbered 930.)
11.5 class.protected paragraph 1 says:
When a friend or a member function of a derived class references a protected nonstatic member of a base class, an access check applies in addition to ...Instead of saying "references a protected nonstatic member of a base class", shouldn't this be rewritten to use the concept of naming class as 11.2 class.access.base paragraph 4 does?
Rationale (04/99): This rule is orthogonal to the specification in 11.2 class.access.base paragraph 4.
From reflector message 8062.
12.2 class.temporary paragraph 4 seems self-contradictory:
the temporary that holds the result of the expression shall persist until the object's initialization is complete... the temporary is destroyed after it has been copied, before or when the initialization completes.How can it be destroyed "before the initialization completes" if it is required to "persist until the object's initialization is complete?"
Rationale (04/00):
It was suggested that "before the initialization completes" refers to the case in which some part of the initialization terminates by throwing an exception. In that light, the apparent contradiction does not apply.
The second paragraph of section 12.7 class.cdtor contains the following text:
To explicitly or implicitly convert a pointer (an lvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior.
Now suppose we have the following piece of code:
struct a { a() : m_a_data(0) { } a(const a& rhsa) : m_a_data(rhsa.m_a_data) { } int m_a_data; }; struct b : virtual a { b() : m_b_data(0) { } b(const b& rhsb) : a(rhsb), m_b_data(rhsb.m_b_data) { } int m_b_data; }; struct c : b { c() : m_c_data(0) { } c(const c& rhsc) : a(rhsc),// Undefined behaviour when constru- // cting an object of type 'c' b(rhsc), m_c_data(rhsc.m_c_data) { } int m_c_data; }; int main() { c ac1, ac2(ac1); }
The problem with the above snippet is that when the value 'ac2' is being created and its construction gets started, c's copy constructor has first to initialize the virtual base class subobject 'a'. Which requires that the lvalue expression 'rhsc' be converted to the type of the parameter of a's copy constructor, which is 'const a&'. According to the wording quoted above, this can be done without undefined behaviour if and only if b's construction has already started, which is not possible since 'a', being a virtual base class, has to be initialized first by a constructor of the most derived object (12.6.2 class.base.init).
The issue could in some cases be alleviated when 'c' has a user-defined copy constuctor. The constructor could default-initialize its 'a' subobject and then initialize a's members as needed taking advantage of the latitude given in paragraph 2 of 12.6.2 class.base.init.
But if 'c' ends up having the implicitly-defined copy constuctor, there's no way to evade undefined behaviour.
struct c : b { c() : m_c_data(0) { } int m_c_data; }; int main() { c ac1, ac2(ac1); }
Paragraph 8 of 12.8 class.copy states
The implicitly-defined copy constructor for class X performs a memberwise copy of its subobjects. The order of copying is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2 class.base.init). Each subobject is copied in the manner appropriate to its type:
- if the subobject is of class type, the copy constructor for the class is used;
Which effectively means that the implicitly-defined copy constructor for 'c' will have to initialize its 'a' base class subobject first and that must be done with a's copy constructor, which will always require a conversion of an lvalue expression of type 'const c' to an lvalue of type 'const a&'. The situation would be the same if all the three classes shown had implicitly-defined copy constructors.
Suggested resolution:
Prepend to paragraph 2 of 12.7 class.cdtor the following:
Unless the conversion happens in a mem-initializer whose mem-initializer-id designates a virtual base class of X, to explicitly or implicitly convert ...
Notes from the 10/01 meeting:
There is no problem in this example. ac1 is fully initialized before it is used in the initialization of ac2.
From reflector message core-7647.
The working paper is quite explicit about
struct X { X(X, X const& = X()); };being illegal (because of the chicken & egg problem wrt copying.)
Shouldn't it be as explicit about the following?
struct Y { Y(Y const&, Y = Y()); };Rationale: There is no need for additional wording. This example leads to a program which either fails to compile (due to resource limits on recursive inlining) or fails to run (due to unterminated recursion). In either case the implementation may generate an error when the program is compiled.
From reflector messages 7864, 7870, 7872, 7997.
The following example does not work as one might expect:
namespace N { class C {}; } int operator +(int i, N::C) { return i+1; } #include <numeric> int main() { N::C a[10]; std::accumulate(a, a+10, 0); }According to 3.4.1 basic.lookup.unqual paragraph 6, I would expect that the "+" call inside std::accumulate would find the global operator+. Is this true, or am I missing a rule? Clearly, the operator+ would be found by Koenig lookup if it were in namespace N.
Daveed Vandevoorde: (core-7870) But doesn't unqualified lookup of the operator+ in the definition of std::accumulate proceed in the namespace where the implicit specialization is generated; i.e., in namespace std?
In that case, you may find a non-empty overload set for operator+ in namespace std and the surrounding (global) namespace is no longer considered?
Nathan Myers: (core-7872) Indeed, <string> defines operator+, as do <complex>, <valarray>, and <iterator>. Any of these might hide the global operator.
Herb Sutter: (core-7997) These examples fail for the same reason:
struct Value { int i; }; typedef map<int, Value > CMap; typedef CMap::value_type CPair; ostream & operator<< ( ostream &os, const CPair &cp ) { return os << cp.first << "/" << cp.second.i; } int main() { CMap courseMap; copy( courseMap.begin(), courseMap.end(), ostream_iterator<CPair>( cout, "\n" ) ); } template<class T, class S> ostream& operator<< (ostream& out, pair<T,S> pr) { return out << pr.first << " : " << pr.second << endl; } int main() { map <int, string> pl; copy( pl.begin(), pl.end(), ostream_iterator <places_t::value_type>( cout, "\n" ) ); }This technique (copying from a map to another container or stream) should work. If it really cannot be made to work, that would seem broken to me. The reason is does not work is that copy and pair are in namespace std and the name lookup rules do not permit the global operator<< to be found because the other operator<<'s in namespace std hide the global operator. (Aside: FWIW, I think most programmers don't realize that a typedef like CPair is actually in namespace std, and not the global namespace.)
Bill Gibbons: It looks like part of this problem is that the library is referring to names which it requires the client to declare in the global namespace (the operator names) while also declaring those names in namespace std. This would be considered very poor design for plain function names; but the operator names are special.
There is a related case in the lookup of operator conversion functions. The declaration of a conversion function in a derived class does not hide any conversion functions in a base class unless they convert to the same type. Should the same thing be done for the lookup of operator function names, e.g. should an operator name in the global namespace be visible in namespace std unless there is a matching declaration in std?
Because the operator function names are fixed, it it much more likely that a declaration in an inner namespace will accidentally hide a declaration in an outer namespace, and the two declarations are much less likely to interfere with each other if they are both visible.
The lookup rules for operator names (when used implicitly) are already quite different from those for ordinary function names. It might be worthwhile to add one more special case.
Mike Ball @Dublin '99 : The original SGI proposal said that non-transitive points of instantiation were also considered. Why, when, and by whom was it added?
Rationale (10/99): This appears to be mainly a program design issue. Furthermore, any attempt to address it in the core language would be beyond the scope of what can be done in a Technical Corrigendum.
From messages 8889-93.
There is a moderately serious problem with the definition of overload resolution. Consider this example:
struct B; struct A { A(B); }; struct B { operator A(); } b; int main() { (void)A(b); }
This is pretty much the definition of "ambiguous," right? You want to convert a B to an A, and there are two equally good ways of doing that: a constructor of A that takes a B, and a conversion function of B that returns an A.
What we discover when we trace this through the standard, unfortunately, is that the constructor is favored over the conversion function. The definition of direct-initialization (the parenthesized form) of a class considers only constructors of that class. In this case, the constructors are the A(B) constructor and the (implicitly-generated) A(const A&) copy constructor. Here's how they are ranked on the argument match:
A(B) | exact match (need a B, have a B) |
A(const A&) | user-defined conversion (B::operator A used to convert B to A) |
In other words, the conversion function does get considered, but it's operating with, in effect, a handicap of one user defined conversion. To put that a different way, this problem is a problem of weighting, not a problem that certain conversion paths are not considered.
I believe the reason that the standard's approach doesn't yield the "intuitive" result is that programmers expect copy constructor elision to be done whenever reasonable, so the intuitive cost of using the conversion function in the example above is simply the cost of the conversion function, not the cost of the conversion function plus the cost of the copy constructor (which is what the standard counts).
Suggested resolution:
In a direct-initialization overload resolution case, if the candidate function being called is a copy constructor and its argument (after any implicit conversions) is a temporary that is the return value of a conversion function, and the temporary can be optimized away, the cost of the argument match for the copy constructor should be considered to be the cost of the argument match on the conversion function argument.
Notes from 10/01 meeting:
It turns out that there is existing practice both ways on this issue, so it's not clear that it is "broken". There is some reason to feel that something that looks like a "constructor call" should call a constructor if possible, rather than a conversion function. The CWG decided to leave it alone.
Can p->f, where f refers to a set of overloaded functions all of which are static member functions, be used as an expression in an address-of-overloaded-function context? A strict reading of this section suggests "no", because "p->f" is not the name of an overloaded function (it's an expression). I'm happy with that, but the core group should decide and should add an example to document the decision, whichever way it goes.
Rationale (10/99): The "strict reading" correctly reflects the intent of the Committee, for the reason given, and no clarification is required.
From messages 8908, 8910-11, and 8913.
It is unclear whether the following code is well-formed or not:
class A { }; struct B : public A { void foo (); void foo (int); }; int main () { void (A::*f)() = (void (A::*)())&B::foo; }
13.4 over.over paragraph 1 says,
The function selected is the one whose type matches the target type required in the context. The target can be
- ...
- an explicit type conversion (5.2.3 expr.type.conv, 5.2.9 expr.static.cast, 5.4 expr.cast).
This would appear to make the program ill-formed, since the type in the cast is different from that of either interpretation of the address-of-member expression (its class is A, while the class of the address-of-member expression is B)
However, 13.4 over.over paragraph 3 says,
Nonstatic member functions match targets of type "pointer-to-member-function;" the function type of the pointer to member is used to select the member function from the set of overloaded member functions.
The class of which a function is a member is not part of the "function type" (8.3.5 dcl.fct paragraph 4). Paragraph 4 is thus either a misuse of the phrase "function type," a contradiction of paragraph 1, or an explanation of what "matching the target type" means in the context of a pointer-to-member target. By the latter interpretation, the example is well-formed and B::foo() is selected.
Bill Gibbons: I think this is an accident due to vague wording. I think the intent was
The function selected is the one which will make the effect of the cast be that of the identity conversion.
Mike Miller: The "identity conversion" reading seems to me to be overly restrictive. It would lead to the following:
struct B { void f(); void f(int); }; struct D: B { }; void (D::* p1)() = &D::f; // ill-formed void (D::* p2)() = (void (B::*)()) &D::f; // okay
I would find the need for an explicit cast here surprising, since the downcast is a standard conversion and since the declaration of p1 certainly has enough information to disambiguate the overload set. (See also issue 203.)
Bill Gibbons: There is an interesting situation with using-declarations. If a base class member function is present in the overload set in a derived class due to a using-declaration, it is treated as if it were a derived class member function for purposes of overload resolution in a call (13.3.1 over.match.funcs paragraph 4):
For non-conversion functions introduced by a using-declaration into a derived class, the function is considered to be a member of the derived class for the purpose of defining the type of the implicit object parameter
There is no corresponding rule for casts. Such a rule would be practical, but if the base class member function were selected it would not have the same class as that specified in the cast. Since base-to-derived pointer to member conversions are implicit conversions, it would seem reasonable to perform this conversion implicitly in this case, so that the result of the cast has the right type. The usual ambiguity and access restrictions on the base-to-derived conversion would not apply since they do not apply to calling through the using-declaration either.
On the other hand, if there is no special case for this, we end up with the bizarre case:
struct A { void foo(); }; struct B : A { using A::foo; void foo(int); } int main() { // Works because "B::foo" contains A::foo() in its overload set. (void (A::*)())&B::foo; // Does not work because "B::foo(int)" does not match the cast. (void (A::*)(int))&B::foo; }
I think the standard should be clarified by either:
Adding a note to 13.4 over.over saying that using-declarations do not participate in this kind of overload resolution; or
Modifying 13.4 over.over saying that using-declarations are treated as members of the derived class for matching purposes, and if selected, the resulting pointer to member is implicitly converted to the derived type with no access or ambiguity errors. (The using-declaration itself has already addressed both of these areas.)
Rationale (10/00): The cited example is well-formed. The function type, ignoring the class specification, is used to select the matching function from the overload set as specified in 13.4 over.over paragraph 3. If the target type is supplied by an explicit cast, as here, the conversion is then performed on the selected pointer-to-member value, with the usual restrictions on what can and cannot be done with the converted value (5.2.9 expr.static.cast paragraph 9, 5.2.10 expr.reinterpret.cast paragraph 9).
From reflector message core-7649.
I understand that the lvalue-to-rvalue conversion was removed in London. I generally agree with this, but it means that ?: needs to be fixed:
Given:
bool test; Integer a, b; test ? a : b;What builtin do we use? The candidates are
operator ?:(bool, const Integer &, const Integer &) operator ?:(bool, Integer, Integer)which are both perfect matches.
(Not a problem in FDIS, but misleading.)
Rationale: The description of the conditional operator in 5.16 expr.cond handles the lvalue case before the prototype is considered.
From reflector messages 8235-36, 8239-40, and c++std-all-2063.
How are default template arguments handled with respect to template template parameters? Two separate questions have been raised:
template <class T, class U = int> class ARG { }; template <class X, template <class Y> class PARM> void f(PARM<X>) { } // specialization permitted? void g() { ARG<int> x; // actually ARG<int, int> f(x); // does ARG (2 parms, 1 with default) // match PARM (1 parm)?Template template parameters are deducible (14.8.2.4 temp.deduct.type paragraph 9), but 14.3.3 temp.arg.template does not specify how matching is done.
Jack Rouse: I implemented template template parameters assuming template signature matching is analogous to function type matching. This seems like the minimum reasonable implementation. The code in the example would not be accepted by this compiler. However, template default arguments are compile time entities so it seems reasonable to relax the matching rules to allow cases like the one in the example. But I would consider this to be an extension to the language.
Herb Sutter: An open issue in the LWG is that the standard doesn't explicitly permit or forbid implementations' adding additional template-parameters to those specified by the standard, and the LWG may be leaning toward explicitly permitting this. [Under this interpretation,] if the standard is ever modified to allow additional template-parameters, then writing "a template that takes a standard library template as a template template parameter" won't be just ugly because you have to mention the defaulted parameters; it would not be (portably) possible at all except possibly by defining entire families of overloaded templates to account for all the possible numbers of parameters vector<> (or anything else) might actually have. That seems unfortunate.
template <template <class T, class U = int> class PARM> class C { PARM<int> pi; };
Jack Rouse: I decided they could not in the compiler I support. This continues the analogy with function type matching. Also, I did not see a strong need to allow default arguments in this context.
A class template used as a template template argument can have default template arguments from its declarations. How are the two sources of default arguments to be reconciled? The default arguments from the template template formal could override. But it could be cofusing if a template-id using the argument template, ARG<int>, behaves differently from a template-id using the template formal name, FORMAL<int>.
Rationale (10/99): Template template parameters are intended to be handled analogously to function function parameters. Thus the number of parameters in a template template argument must match the number of parameters in a template template parameter, regardless of whether any of those paramaters have default arguments or not. Default arguments are allowed for the parameters of a template template parameter, and those default arguments alone will be considered in a specialization of the template template parameter within a template definition; any default arguments for the parameters of a template template argument are ignored.
From reflector messages 8049-8052, 8054-8058.
According to 14.5.2 temp.mem paragraph 4,
A specialization of a member function template does not override a virtual function from a base class.Bill Gibbons: I think that's sufficiently surprising behavior that it should be ill-formed instead.
As I recall, the main reason why a member function template cannot be virtual is that you can't easily construct reasonable vtables for an infinite set of functions. That doesn't apply to overrides.
Another problem is that you don't know that a specialization overrides until the specialization exists:
struct A { virtual void f(int); }; struct B : A { template<class T> void f(T); // does this override? };But this could be handled by saying:
template<int I> struct X; struct A { virtual void f(A<5>); }; struct B : A { template<int I, int J> void f(A<I+J>); // does not overrride }; void g(B *b) { X<t> x; b->f<3,2>(x); // specialization B::f(A<5>) makes program ill-formed }So I think there are reasonable semantics. But is it useful?
If not, I think the creation of a specialization that would have been an override had it been declared in the class should be an error.
Daveed Vandevoorde: There is real code out there that is written with this rule in mind. Changing the standard on them would not be good form, IMO.
Mike Ball: Also, if you allow template functions to be specialized outside of the class you introduce yet another non-obvious ordering constraint.
Please don't make such a change after the fact.
John Spicer: This is the result of an explicit committee decision. The reason for this rule is that it is too easy to unwittingly override a function from a base class, which was probably not what was intended when the template was written. Overriding should be a conscious decision by the class writer, not something done accidentally by a template.
Rationale (10/99): The Standard correctly reflects the intent of the Committee.
Issue 1
Paragraph 1 says that a friend of a class template can be a template. Paragraph 2 says: A friend template may be declared within a non-template class. A friend function template may be defined within a non-template class.
I'm not sure what this wording implies about friend template definitions within template classes. The rules for class templates and normal classes should be the same: a function template can be declared or defined, but a class template can only be declared in a friend declaration.
Issue 2
Paragraph 4 says: When a function is defined in a friend function declaration in a class template, the function is defined when the class template is first instantiated. I take it that this was intended to mean that a function that is defined in a class template is not defined until the first instantiation. I think this should say that a function that is defined in a class template is defined each time the class is instantiated. This means that a function that is defined in a class template must depend on all of the template parameters of the class template, otherwise multiple definition errors could occur during instantiations. If we don't have a rule like this, compilers would have to compare the definitions of functions to see whether they are the same or not. For example:
template <class T> struct A { friend int f() { return sizeof(T); } }; A<int> ai; A<long> ac;I hope we would all agree that this program is ill-formed, even if long and int have the same size.
From Bill Gibbons:
[1] That sounds right.
[2] Whenever possible, I try to treat instantiated class templates as if they were ordinary classes with funny names. If you write:
struct A_int { friend int f() { return sizeof(int); } }; struct A_long { friend int f() { return sizeof(long); } };it is a redefinition (which is not allowed) and an ODR violation. And if you write:
template <class T, class U> struct A { friend int f() { return sizeof(U); } }; A<int,float> ai; A<long,float> ac;the corresponding non-template code would be:
struct A_int_float { friend int f() { return sizeof(float); } }; struct A_long_float { friend int f() { return sizeof(float); } };then the two definitions of "f" are identical so there is no ODR violation, but it is still a redefinition. I think this is just an editorial clarification.
Rationale (04/99): The first sub-issue reflects wording that was changed to address the concern before the IS was issued. A close and careful reading of the Standard already leads to the conclusion that the example in the second sub-issue is ill-formed, so no change is needed.
From reflector message core-7772.
Does Koenig lookup create a point of instantiation for class types? I.e., if I say:
TT<int> *p; f(p);The namespaces and classes associated with p are those associated with the type pointed to, i.e., TT<int>. However, to determine those I need to know TT<int> bases and its friends, which requires instantiation.
Or should this be special cased for templates?
Rationale: The standard already specifies that this creates a point of instantiation.
Is the second explicit instantiation below well-formed?
template <class T> struct A { template <class T2> void f(T2){} }; template void A<int>::f(char); // okay template template void A<int>::f(float); // ?Since multiple "template<>" clauses are permitted in an explicit specialization, it might follow that multiple "template" keywords should also be permitted in an explicit instantiation. Are multiple "template" keywords not allowed in an explicit instantiation? The grammar permits it, but the grammar permits lots of stuff far weirder than that. My opinion is that, in the absence of explicit wording permitting that kind of usage (as is present for explicit specializations) that such usage is not permitted for explicit instantiations.
Rationale (04/99): The Standard does not describe the meaining of multiple template keywords in this context, so the example should be considered as resulting in undefined behavior according to 1.3.12 defns.undefined .
(Previously numbered 839.)
[N1065 issue 1.19] An explicit specialization declaration may not be visible during instantiation under the template compilation model rules, even though its existence must be known to perform the instantiation correctly. For example:
translation unit #1
template<class T> struct A { }; export template<class T> void f(T) { A<T> a; }translation unit #2
template<class T> struct A { }; template<> struct A<int> { }; // not visible during instantiation template<class T> void f(T); void g() { f(1); }Rationale: This issue was addressed in the FDIS and should have been closed.
Is this valid C++? The question is whether a member constant can be specialized. My inclination is to say no.
template <class T> struct A { static const T i = 0; }; template<> const int A<int>::i = 42; int main () { return A<int>::i; }John Spicer: This is ill-formed because 9.4.2 class.static.data paragraph 4 prohibits an initializer on a definition of a static data member for which an initializer was provided in the class.
The program would be valid if the initializer were removed from the specialization.
Daveed Vandevoorde: Or at least, the specialized member should not be allowed in constant-expressions.
Bill Gibbons: Alternatively, the use of a member constant within the definition could be treated the same as the use of "sizeof(member class)". For example:
template <class T> struct A { static const T i = 1; struct B { char b[100]; }; char x[sizeof(B)]; // specialization can affect array size char y[i]; // specialization can affect array size }; template<> const int A<int>::i = 42; template<> struct A<int>::B { char z[200] }; int main () { A<int> a; return sizeof(a.x) // 200 (unspecialized value is 100) + sizeof(a.y); // 42 (unspecialized value is 1) }For the member template case, the array size "sizeof(B)" cannot be evaluated until the template is instantiated because B might be specialized. Similarly, the array size "i" cannot be evaluated until the template is instantiated.
Rationale (10/99): The Standard is already sufficiently clear on this question.
The Standard does not describe how to handle an example like the following:
template <class T> int f(T, int); template <class T> int f(int, T); template<> int f<int>(int, int) { /*...*/ }
It is impossible to determine which of the function templates is being specialized. This problem is related to the discussion of issue 229, in which one of the objections raised against partial specialization of function templates is that it is not possible to determine which template is being specialized.
Notes from 10/01 meeting:
It was decided that while this is true, it's not a problem. You can't call such functions anyway; the call would be ambiguous.
Consider:
template <class T> void f(T&); template <class T> void f(const T&); void m() { const int p = 0; f(p); }Some compilers treat this as ambiguous; others prefer f(const T&). The question turns out to revolve around whether 14.8.2.1 temp.deduct.call paragraph 2 says what it ought to regarding the removal of cv-qualifiers and reference modifiers from template function parameters in doing type deduction.
John Spicer: The partial ordering rules as originally proposed specified that, for purposes of comparing parameter types, you remove a top level reference, and after having done that you remove top level qualifiers. This is not what is actually in the IS however. The IS says that you remove top level qualifiers and then top level references.
The original rules were intended to prefer f(A<T>) over f(const T&).
Rationale (10/99): The Standard correctly reflects the intent of the Committee.
The grammar should be changed so that constructor function-try-blocks must end with a throw-expression.
Rationale (04/00):
No change is needed. It is already the case that flowing off the end of a function-try-block of a constructor rethrows the exception, and return statements are prohibited in constructor function-try-blocks (15.3 except.handle paragraphs 15-16.
From reflector message core-8002.
Questions regarding when a throw-expression temporary object is destroyed.
Section 15.1 except.throw paragraph 4 describes when the temporary is destroyed when a handler is found. But what if no handler is found:
struct A { A() { printf ("A() \n"); } A(const A&) { printf ("A(const A&)\n"); } ~A() { printf ("~A() \n"); } }; void t() { exit(0); } int main() { std::set_terminate(t); throw A(); }Does A::~A() ever execute here? (Or, in case two constructions are done, are there two destructions done?) Is it implementation-defined, analogously to whether the stack is unwound before terminate() is called (15.3 except.handle paragraph 9)?
Or what if an exception specification is violated? There are several different scenarios here:
int glob = 0; // or 1 or 2 or 3 struct A { A() { printf ("A() \n"); } A(const A&) { printf ("A(const A&)\n"); } ~A() { printf ("~A() \n"); } }; void u() { switch (glob) { case 0: exit(0); case 1: throw "ok"; case 2: throw 17; default: throw; } } void foo() throw(const char*, std::bad_exception) { throw A(); } int main() { std::set_unexpected(u); try { foo(); } catch (const char*) { printf("in handler 1\n"); } catch (std::bad_exception) { printf("in handler 2\n"); } }The case where u() exits is presumably similar to the terminate() case. But in the cases where the program goes on, A::~A() should be called for the thrown object at some point. But where does this happen? The standard doesn't really say. Since an exception is defined to be "finished" when the unexpected() function exits, it seems to me that is where A::~A() should be called — in this case, as the throws of new (or what will become new) exceptions are made out of u(). Does this make sense?
Rationale (10/99): Neither std::exit(int) nor std::abort() destroys temporary objects, so the exception temporary is not destroyed when no handler is found. The original exception object is destroyed when it is replaced by an unexpected() handler. The Standard is sufficiently clear on these points.
15.3 except.handle paragraph 3 contains the following text:
A handler is a match for a throw-expression with an object of type E if
- The handler is of type cv T or cv T& and E and T are the same type (ignoring the top-level cv- qualifiers), or
- the handler is of type cv T or cv T& and T is an unambiguous public base class of E, or
- the handler is of type cv1 T* cv2 and E is a pointer type that can be converted to the type of the handler by either or both of
- a standard pointer conversion (4.10 conv.ptr) not involving conversions to pointers to private or protected or ambiguous classes
- a qualification conversion
I propose to alter this text to allow to catch exceptions with ambiguous public base classes by some of the public subobjects. I'm really sure that if someone writes:
try { // ... } catch (Matherr& m) { // ... }he really wants to catch all Matherrs rather than to allow some of the Matherrs to escape:
class SomeMatherr : public Matherr { /* */ }; struct TrickyBase1 : public SomeMatherr { /* */ }; struct TrickyBase2 : public SomeMatherr { /* */ }; struct TrickyMatherr : public TrickyBase1, TrickyBase2 { /* */ };
According to the standard TrickyMatherr will leak through the catch (Matherr& m) clause. For example:
#include <stdio.h> struct B {}; struct B1 : B {}; struct B2 : B {}; struct D : B1, B2 {}; // D() has two B() subobjects void f() { throw D(); } int main() { try { f(); } catch (B& b) { puts("B&"); } // passed catch (D& d) { puts("D&"); } // really works _after_ B&!!! }
Also I see one more possible solution: to forbid objects with ambiguous base classes to be "exceptional objects" (for example Borland C++ goes this way) but it seems to be unnecessary restrictive.
Notes from the 10/01 meeting:
The Core Working Group did not feel this was a significant problem. Catching either of the ambiguous base classes would be surprising, and giving an error on throwing an object that has an ambiguous base class would break existing code.
From reflector message core-7806:
The term "throw exception" seems to sometimes refer to an expression of the form "throw expr" and sometimes just to the "expr" portion thereof.
As a result it is not quite clear to me whether when "uncaught_exception()" becomes true: before or after the temporary copy of the value of "expr".
Is there a definite consensus about that?
Rationale: The standard is sufficiently clear; the phrase "to be thrown" indicates that the throw itself (which includes the copy to the temporary object) has not yet begun. The footnote in 15.5.1 except.terminate paragraph 1 reinforces this ordering.
The grammar in Appendix A does not indicate a grammar sentence symbol and is therefore formally not a grammar.
Rationale (04/01):
Appendix A does not claim to be a formal grammar. The specification is clear enough in its current formulation.
From reflector message core-7883.
Annex C lists C compatibility issues. One item not in the annex came up in a discussion in comp.std.c++.
Consider this C and C++ code:
const int j = 0; char* p = (char*)j;
Rationale (10/99): Because j is not a constant expression in C, this code fragment has implementation-defined behavior in C. There is no incompatibility with C resulting from the fact that C++ defines this behavior.
D.2 depr.static says that declaring namespace-scope objects as static is deprecated. Declaring namespace-scope functions as static should also be deprecated.
Proposed resolution (10/99): In both 7.3.1.1 namespace.unnamed paragraph 2 and D.2 depr.static paragraph 1, replace
when declaring objects in a namespace scopewith
when declaring entities in a namespace scopeIn addition, there are a number of locations in the Standard where use of or reference to static should be reconsidered. These include:
Rationale (04/00):
This issue, along with issue 174, has been subsumed by issue 223. Until the committee determines the meaning of deprecation, it does not make sense either to extend or reduce the number of features to which it is applied.
The decision to deprecate global static should be reversed.
Rationale (04/00):
This issue, along with issue 167, has been subsumed by issue 223. Until the committee determines the meaning of deprecation, it does not make sense either to extend or reduce the number of features to which it is applied.
From reflector message core-7938.
The wording in 3.2 basic.def.odr paragraph 2 about "potentially evaluated" is incomplete. It does not distinguish between expressions which are used as "integral constant expressions" and those which are not; nor does it distinguish between uses in which an objects address is taken and those in which it is not. (A suitable definition of "address taken" could be written without actually saying "address".)
Currently the definition of "use" has two parts (part (a) and (d) below); but in practice there are two more kinds of "use" as in (b) and (c):
I don't think we discussed (c).
Rationale (04/99): The substantive part of this issue is covered by Core issue 48
(Previously numbered 915.)
Given the following test case:
enum E { e1, e2, e3 }; void f(int, E e = e1); void f(E, E e = e1); void g() { void f(long, E e = e2); f(1); // calls ::f(int, E) f(e1); // ? }First note that Koenig lookup breaks the concept of hiding functions through local extern declarations as illustrated by the call `f(1)'. Should the WP show this as an example?
Second, it appears the WP is silent as to what happens with the call `f(e1)': do the different default arguments create an ambiguity? is the local choice preferred? or the global?
Tentative Resolution (10/98) In 3.4.2 basic.lookup.koenig paragraph 2, change
If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered.to
If the ordinary unqualified lookup of the name finds the declaration of a class member function or the declaration of a function at block scope, the associated namespaces and classes are not considered.
Rationale (04/99): The proposal would also apply to local using-declarations (per Mike Ball) and was therefore deemed undesirable. The ambiguity issue is dealt with in Core issue 1
From reflector messages core-7876-7878.
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 Ball: This makes sense, but I couldn't find much support in the document. Sounds like yet another interpretation to add to the list.
John Spicer: 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.
Mike Ball: 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.
John Spicer: 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.
"export" is an additional attribute that is separate from linkage, but that can only be applied to templates with external linkage.
Mike Ball: 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 Spicer: 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.
Rationale: Duplicate of Core issue 69.
From reflector messages 8509, 8511, and 8513-14.
The description of how the partial ordering of template functions is determined in 14.5.5.2 temp.func.order paragraphs 3-5 does not make any provision for nondeduced template parameters. For example, the function call in the following code is ambiguous, even though one template is "obviously" more specialized than the other:
template <class T> T f(int); template <class T, class U> T f(U); void g() { f<int>(1); }The reason is that neither function parameter list allows template parameter T to be deduced; both deductions fail, so neither template is considered more specialized than the other and the function call is ambiguous.
One possibility of addressing this situation would be to incorporate explicit template arguments from the call in the argument deduction using the transformed function parameter lists. In this case, that would result in finding the first template to be more specialized than the second.
Rationale (04/00):
This issue is covered in a more general context in issue 214.
From reflector messages 8158 and 8168.
15.4 except.spec paragraph 1 says,
An exception-specification shall appear only on a function declarator in a function, pointer, reference or pointer to member declaration or definition.This wording forbids exception specifications in declarations where they might plausibly occur (e.g., an array of function pointers). This restriction seems arbitrary. It's also unclear whether this wording allows declarations such as
void (*f())() throw(int); // returns a pointer to a function // that might throw "int"
At the same time, other cases are allowed by the wording in paragraph 1 (e.g., a pointer to a pointer to a function), but no checking for such cases is specified in paragraph 3. For example, the following appears to be allowed:
void (*p)() throw(int); void (**pp)() throw() = &p;
Rationale (10/99): Duplicate of issues 87 and 92.
From reflector message core-7932.
The example in 18.4.1.3 lib.new.delete.placement reads:
[Example: This can be useful for constructing an object at a known address:This example has potential alignment problems. One way to correct it would be to change the definition of place to read:char place[sizeof(Something)]; Something* p = new (place) Something();—end example]
char* place = new char[sizeof(Something)];
Rationale (10/99): This is an issue for the Library Working Group.
(Previously numbered 914.)
Issue 1:
The working paper is not clear about how the typename/template keywords interact with using-declarations:
template<class T> struct A { typedef int X; }; template<class T> void f() { typename A<T>::X a; // OK using typename A<T>::X; // OK typename X b; // ill-formed; X must be qualified X c; // is this OK? }When the rules for typename and the similar use of template were decided, we chose to require that they be used at every reference. The way to avoid typename at every use is to declare a typedef; then the typedef name itself is known to be a type. For using-declarations, we decided that they do not introduce new declarations but rather are aliases for existing declarations, like symbolic links. This makes it unclear whether the declaration "X c;" above should be well-formed, because there is no new name declared so there is no declaration with a "this is a type" attribute. (The same problem would occur with the template keyword when a member template of a dependent class is used). I think these are the main options:
The core WG already resolved this issue according to (1), but the wording does not seem to have been added to the standard. New wording needs to be drafted.
Issue 2:
Either way, one more point needs clarification. If the first option is adopted:
template<class T> struct A { struct X { }; }; template<class T> void g() { using typename A<T>::X; X c; // if this is OK, then X by itself is a type int X; // is this OK? }When "g" is instantiated, the two declarations of X are compatible (7.3.3 namespace.udecl paragraph 10). But there is no way to know this when the definition of "g" is compiled. I think this case should be ill-formed under the first option. (It cannot happen under the second option.) If the second option is adopted:
template<class T> struct A { struct X { }; }; template<class T> void g() { using A<T>::X; int X; // is this OK? }Again, the instantiation would work but there is no way to know that in the template definition. I think this case should be ill-formed under the second option. (It would already be ill-formed under the first option.)
From John Spicer:
The "not a new declaration" decision is more of a guiding principle than a hard and fast rule. For example, a name introduced in a using-declaration can have different access than the original declaration.Tentative Resolution:Like symbolic links, a using-declaration can be viewed as a declaration that declares an alias to another name, much like a typedef.
In my opinion, "X c;" is already well-formed. Why would we permit typename to be used in a using-declaration if not to permit this precise usage?
In my opinion, all that needs to be done is to clarify that the "typeness" or "templateness" attribute of the name referenced in the using-declaration is attached to the alias created by the using-declaration. This is solution #1.
The rules for multiple declarations with the same name in the same scope should treat a using-declaration which names a type as a typedef, just as a typedef of a class name is treated as a class declaration. This needs drafting work. Also see Core issue 36.
Rationale (04/99): Any semantics associated with the typename keyword in using-declarations should be considered an extension.
Daveed Vandevoorde in core-8008 : While reading Core issue 11 I thought it implied the following possibility:
template<typename T> struct B { template<int> void f(int); }; template<typename T> struct D: B<T> { using B<T>::template f; void g() { this->f<1>(0); } // OK, f is a template };
However, the grammar for a using-declaration reads:
and nested-name-specifier never ends in "template".
Is that intentional?
Bill Gibbons in core-8010 :
It certainly appears to be, since we have:
Rationale (04/99): Any semantics associated with the template keyword in using-declarations should be considered an extension.
(Previously numbered 921.)
See also reflector messages 8624-6, 8628, and 8630.
How can we write a function template, or member function of a class template that takes a C linkage function as a parameter when the function type depends on one of the template parameter types?
extern "C" void f(int); void g(char); template <class T> struct A { A(void (*fp)(T)); }; A<char> a1(g); // okay A<int> a2(f); // errorAnother variant of the same problem is:
extern "C" void f(int); void g(char); template <class T> void h( void (*fp)(T) ); int main() { h(g); // okay h(f); // error }
Somehow permit a language linkage to be specified as part of a function parameter declaration. i.e.
template <class T> struct A { A( extern "C" void (*fp)(T) ); }; template <class T> void h( extern "C" void (*fp)(T) );Suggested resolution: (Bill Gibbons)
The whole area of linkage needs revisiting. Declaring calling convention as a storage class was incorrect to begin with; it should be a function qualifier, as in:
void f( void (*pf)(int) c_linkage );instead of the suggested:
void f( extern "C" void (*pf)(int) );I would like to keep calling convention on the "next round" issues list, including the alternative of using function qualifiers.
And to that end, I suggest that the use of linkage specifiers to specify calling convention be deprecated - which would make any use of linkage specifiers in a parameter declaration deprecated.
Martin Sebor: 7.5 dcl.link, paragraph 4 says that "A linkage-specification shall occur only in namespace scope..." I'm wondering why this restriction is necessary since it prevents, among other things, the use of the functions defined <cmath> in generic code that involves function objects. For example, the program below is ill-formed since std::pointer_to_binary_function<> takes a pointer to a function with extern "C++" linkage which is incompatible with the type of the double overload of std::pow.
Relaxing the restriction to allow linkage specification in declarations of typedefs in class scope would allow std::pointer_to_binary_function<> ctor to be overloaded on both types (i.e., extern "C" and extern "C++"). An alternative would be to allow for the linkage specification to be deduced along with the type.
#include <cmath> #include <functional> #include <numeric> int main () { double a[] = { 1, 2, 3 }; return std::accumulate (a, a + 3, 2.0, std::pointer_to_binary_function<double, double, double>(std::pow)); }
(Previously numbered 923.)
8.3.6 dcl.fct.default paragraph 4 says:
For non-template functions, default arguments can be added in later declarations of a functions in the same scope.Why say for non-template functions? Why couldn't the following allowed?
template <class T> struct B { template <class U> inline void f(U); }; template <class T> template <class U> inline void B<T>::f(U = int) {} // adds default arguments // is this well-formed? void g() { B<int> b; b.f(); }If this is ill-formed, chapter 14 should mention this.
Rationale: This is sufficiently clear in the standard. Allowing additional default arguments would be an extension.
(Previously numbered 876b.)
(See also paper J16/99-0005 = WG21 N1182.)At the London meeting, 12.8 class.copy paragraph 15 was changed to limit the optimization described to only the following cases:
Can we find an appropriate description for the desired cases?
Rationale (04/99): The absence of this optimization does not constitute a defect in the Standard, although the proposed resolution in the paper should be considered when the Standard is revised.