| Document number: | J16/06-0067 = WG21 N1997 |
| Date: | 2006-04-22 |
| Project: | Programming Language C++ |
| Reference: | ISO/IEC IS 14882:2003 |
| Reply to: | William M. Miller |
| Edison Design Group, Inc. | |
| wmm@edg.com |
This document contains the C++ core language issues on which the Committee (J16 + WG21) has not yet acted, that is, issues with status "Ready," "Review," "Drafting," and "Open."
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:
The purpose of these documents is to record the disposition of issues which have come before the Core Language Working Group of the ANSI (J16) and ISO (WG21) C++ Standard Committee.
Issues represent potential defects in the ISO/IEC IS 14882:2003 document and corrected defects in the earlier ISO/IEC 14882:1998 document; they are not necessarily formal ISO Defect Reports (DRs). While some issues will eventually be elevated to DR status, others will be disposed of in other ways. (See Issue Status below.)
The most current public version of this document can be found at http://www.open-std.org/jtc1/sc22/wg21. Requests for further information about these documents should include the document number, reference ISO/IEC 14882:2003, and be submitted to the InterNational Committee for Information Technology Standards (INCITS), 1250 Eye Street NW, Suite 200, Washington, DC 20005, USA.
Information regarding how to obtain a copy of the C++ Standard, join the Standard Committee, or submit an issue can be found in the C++ FAQ at http://www.jamesd.demon.co.uk/csc/faq.html. Public discussion of the C++ Standard and related issues occurs on newsgroup comp.std.c++.
Issues progress through various statuses as the Core Language Working Group and, ultimately, the full J16 and WG21 committees deliberate and act. For ease of reference, issues are grouped in these documents by their status. Issues have one of the following statuses:
Open: The issue is new or the working group has not yet formed an opinion on the issue. If a Suggested Resolution is given, it reflects the opinion of the issue's submitter, not necessarily that of the working group or the Committee as a whole.
Drafting: Informal consensus has been reached in the working group and is described in rough terms in a Tentative Resolution, although precise wording for the change is not yet available.
Review: Exact wording of a Proposed Resolution is now available for an issue on which the working group previously reached informal consensus.
Ready: The working group has reached consensus that the issue is a defect in the Standard, the Proposed Resolution is correct, and the issue is ready to forward to the full Committee for ratification as a proposed defect report.
DR: The full Committee has approved the item as a proposed defect report. The Proposed Resolution in an issue with this status reflects the best judgment of the Committee at this time regarding the action that will be taken to remedy the defect; however, the current wording of the Standard remains in effect until such time as a Technical Corrigendum or a revision of the Standard is issued by ISO.
TC1: A DR issue included in Technical Corrigendum 1. TC1 is a revision of the Standard issued in 2003.
WP: A DR issue whose resolution is reflected in the current Working Paper. The Working Paper is a draft for a future version of the Standard.
Dup: The issue is identical to or a subset of another issue, identified in a Rationale statement.
NAD: The working group has reached consensus that the issue is not a defect in the Standard. A Rationale statement describes the working group's reasoning.
Extension: The working group has reached consensus that the issue is not a defect in the Standard but is a request for an extension to the language. The working group expresses no opinion on the merits of an issue with this status; however, the issue will be maintained on the list for possible future consideration as an extension proposal.
The current wording of 2.13.2 lex.ccon paragraph 3 states,
If the character following a backslash is not one of those specified, the behavior is undefined.
Paper J16/04-0167=WG21 N1727 suggests that such character escapes be ill-formed. In discussions at the Lillehammer meeting, however, the CWG felt that the newly-approved category of conditionally-supported behavior would be more appropriate.
Proposed resolution (April, 2006):
Change the next-to-last sentence of 2.13.2 lex.ccon paragraph 3 from:
If the character following a backslash is not one of those specified, the behavior is undefined.
to:
Escape sequences in which the character following the backslash is not listed in Table 6 are conditionally-supported, with implementation-defined semantics.
Is the following code well-formed?
namespace N {
int i;
extern int j;
}
int N::j = i;
The question here is whether the lookup for i in the initializer of N::j finds the declaration in namespace N or not. Implementations differ on this question.
If N::j were a static data member of a class, the answer would be clear: both 3.4.1 basic.lookup.unqual paragraph 12 and 8.5 dcl.init paragraph 11 say that the initializer “is in the scope of the member's class.” There is no such provision for namespace members defined outside the namespace, however.
The reasoning given in 3.4.1 basic.lookup.unqual may be instructive:
A name used in the definition of a static data member of class X (9.4.2 class.static.data) (after the qualified-id of the static member) is looked up as if the name was used in a member function of X.
It is certainly the case that a name used in a function that is a member of a namespace is looked up in that namespace (3.4.1 basic.lookup.unqual paragraph 6), regardless of whether the definition is inside or outside that namespace. Initializers for namespace members should probably be looked up the same way.
Proposed resolution (April, 2006):
Add a new paragraph following 3.4.1 basic.lookup.unqual paragraph 12:
If a variable member of a namespace is defined outside of the scope of its namespace then any name used in the definition of the variable member (after the declarator-id) is looked up as if the definition of the variable member occurred in its namespace. [Example:
namespace N { int i = 4; extern int j; } int i = 2; int N::j = i; // N::j == 4—end example]
One might assume from 14.7.1 temp.inst paragraph 1 that argument-dependent lookup would require instantiation of any class template specializations used in argument types:
Unless a class template specialization has been explicitly instantiated (14.7.2 temp.explicit) or explicitly specialized (14.7.3 temp.expl.spec), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program.
A complete class type is required to determine the associated classes and namespaces for the argument type (to determine the class's bases) and to determine the friend functions declared by the class, so the completeness of the class type certainly “affects the semantics of the program.”
This conclusion is reinforced by the second bullet of 3.4.2 basic.lookup.argdep paragraph 2:
If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces in which its associated classes are defined.
A class template specialization is a class type, so the second bullet would appear to apply, requiring the specialization to be instantiated in order to determine its base classes.
However, bullet 8 of that paragraph deals explicitly with class template specializations:
If T is a class template specialization its associated namespaces and classes are the namespace in which the template is defined; for member templates, the member template’s class; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which any template template arguments are defined; and the classes in which any member templates used as template template arguments are defined.
Note that the class template specialization itself is not listed as an associated class, unlike other class types, and there is no mention of base classes. If bullet 8 were intended as a supplement to the treatment of class types in bullet 2, one would expect phrasing along the lines of, “In addition to the associated namespaces and classes for all class types...” or some such; instead, bullet 8 reads like a self-contained and complete specification.
If argument-dependent lookup does not cause implicit instantiation, however, examples like the following fail:
template <typename T> class C {
friend void f(C<T>*) { }
};
void g(C<int>* p) {
f(p); // found by ADL??
}
Implementations differ in whether this example works or not.
Proposed resolution (April, 2006):
Change bullet 2 of 3.4.2 basic.lookup.argdep paragraph 2 as indicated:
If T is a class type (including unions), its associated
classes are: the class itself; the class of which it is a member, if
any; and its direct and indirect base classes. Its associated
namespaces are the namespaces in of which its associated
classes are defined members. Furthermore,
if T is a class template specialization, its associated
namespaces and classes also include: the namespaces and classes
associated with the types of the template arguments provided for
template type parameters (excluding template template parameters); the
namespaces of which any template template arguments are members; and
the classes of which any member templates used as template template
arguments are members. [Note: Non-type template arguments do not
contribute to the set of associated namespaces. —end
note]
Delete bullet 8 of 3.4.2 basic.lookup.argdep paragraph 2:
If T is a class template specialization its associated namespaces and classes are the namespace in which the template is defined; for member templates, the member template’s class; the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces in which any template template arguments are defined; and the classes in which any member templates used as template template arguments are defined. [Note: non-type template arguments do not contribute to the set of associated namespaces. —end note]
I believe this program is invalid:
struct A {
};
struct C {
struct A {};
void f ();
};
void C::f () {
::A *a;
a->~A ();
}
The problem is that 3.4.5
basic.lookup.classref says that you have to look
up A in both the context of the pointed-to-type (i.e.,
::A), and
in the context of the postfix-expression (i.e., the body of C::f), and
that if the name is found in both places it must name the same type in
both places.
The EDG front end does not issue an error about this program, though.
Am I reading the standardese incorrectly?
John Spicer: I think you are reading it correctly. I think I've been hoping that this would get changed. Unlike other dual lookup contexts, this is one in which the compiler already knows the right answer (the type must match that of the left hand of the -> operator). So I think that if either of the types found matches the one required, it should be sufficient. You can't say a->~::A(), which means you are forced to say a->::A::~A(), which disables the virtual mechanism. So you would have to do something like create a local typedef for the desired type.
See also issues 244, 399, and 466.
Proposed resolution (April, 2006):
Remove the indicated text from 3.4.5 basic.lookup.classref paragraph 2:
If the id-expression in a class member access (5.2.5 expr.ref) is an unqualified-id, and the type of the object expression is of a class type C(or of pointer to a class type C), the unqualified-id is looked up in the scope of class C...
Change 3.4.5 basic.lookup.classref paragraph 3 as indicated:
If the unqualified-id is ~type-name, the type-name is looked up in the context of the entire postfix-expression.andIf the type T of the object expression is of a class type C(or of pointer to a class type C), the type-name is also looked upin the context of the entire postfix-expression andin the scope of class C.The type-name shall refer to a class-name. If type-name is found in both contexts, the name shall refer to the same class type. If the type of the object expression is of scalar type, the type-name is looked up in the scope of the complete postfix-expression.At least one of the lookups shall find a name that refers to (possibly cv-qualified) T. [Example:struct A { }; struct B { struct A { }; void f(::A* a); }; void B::f(::A* a) { a->~A(); // OK, lookup in *a finds the injected-class-name }—end example]
[Note: this change also resolves issue 414.]
By 3.4.5 basic.lookup.classref paragraph 3, the following is ill-formed because the two lookups of the destructor name (in the scope of the class of the object and in the surrounding context) find different Xs:
struct X {};
int main() {
X x;
struct X {};
x.~X(); // Error?
}
This is silly, because the compiler knows what the type has to be, and one of the things found matches that. The lookup should require only that one of the lookups finds the required class type.
Proposed resolution (April, 2005):
This issue is resolved by the resolution of issue 305.
According to 3.7.3.1 basic.stc.dynamic.allocation paragraph 3,
Any other allocation function that fails to allocate storage shall only indicate failure by throwing an exception of class std::bad_alloc (18.4.2.1 lib.bad.alloc) or a class derived from std::bad_alloc.
Shouldn't this statement have the usual requirements for an unambiguous and accessible base class?
Proposed resolution (April, 2006):
Change the last sentence of 3.7.3.1 basic.stc.dynamic.allocation paragraph 3 as indicated:
Any other allocation function that fails to allocate storage shallonlyindicate failure only by throwing an exception ofclass std::bad_alloc (18.4.2.1 lib.bad.alloc) or a class derived from std::bad_alloca type that would match a handler (15.3 except.handle) of type std::bad_alloc (18.4.2.1 lib.bad.alloc).
When the Standard refers to a virtual base class, it should be understood to include base classes of virtual bases. However, the Standard doesn't actually say this anywhere, so when 4.11 conv.mem (for example) forbids casting to a derived class member pointer from a virtual base class member pointer, it could be read as meaning:
struct B {};
struct D : public B {};
struct D2 : virtual public D {};
int B::*p;
int D::*q;
void f() {
static_cast<int D2::*>(p); // permitted
static_cast<int D2::*>(q); // forbidden
}
Proposed resolution (October, 2005):
Change 4.11 conv.mem paragraph 2 as indicated:
...If B is an inaccessible (clause 11 class.access), ambiguous (10.2 class.member.lookup) or virtual (10.1 class.mi) base class of D, or a base class of a virtual base class of D, a program that necessitates this conversion is ill-formed...
Change 5.2.9 expr.static.cast paragraph 2 as indicated:
...and B isnotneither a virtual base class of D nor a base class of a virtual base class of D...
Change 5.2.9 expr.static.cast paragraph 9 as indicated:
...and B isnotneither a virtual base class of D nor a base class of a virtual base class of D...
The current wording of 5.2.2 expr.call paragraph 7 states:
When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (18.7 lib.support.runtime). The lvalue-to-rvalue (4.1 conv.lval), array-to-pointer (4.2 conv.array), and function-to-pointer (4.3 conv.func) standard conversions are performed on the argument expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type (clause 9 class), the behavior is undefined.
Paper J16/04-0167=WG21 N1727 suggests that passing a non-POD object to ellipsis be ill-formed. In discussions at the Lillehammer meeting, however, the CWG felt that the newly-approved category of conditionally-supported behavior would be more appropriate.
Proposed resolution (October, 2005):
Change 5.2.2 expr.call paragraph 7 as indicated:
...After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed.If the argument has a non-POD class type (clause 9), the behavior is undefined.Passing an argument of non-POD class type (clause 9) with no corresponding parameter is conditionally-supported, with implementation-defined semantics.
The following translation unit appears to be well-formed.
int x[true?throw 4:5];
According to 5.19 expr.const, this appears to be an integral constant expression: it is a conditional expression, involves only literals, and no assignment, increment, decrement, function-call, or comma operators. However, if this is well-formed, the standard gives no meaning to this declaration, since the array bound (8.3.4 dcl.array paragraph 1) cannot be computed.
I believe the defect is that throw expressions should also be banned from constant expressions.
Notes from October 2002 meeting:
We should also check on new and delete.
Notes from the April, 2005 meeting:
Although it could be argued that all three of these operators potentially involve function calls — throw to std::terminate, new and delete to the corresponding allocation and deallocation functions — and thus would already be excluded from constant expressions, this reasoning was considered to be too subtle to allow closing the issue with no change. A modification that explicitly clarifies the status of these operators will be drafted.
Proposed resolution (October, 2005):
Change the last sentence of 5.19 expr.const as indicated:
In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement,function-callfunction call (including new-expressions and delete-expressions),orcomma operators, or throw-expressions shall not be used.
Note: this sentence is also changed by the resolution of issue 530.
I couldn't find wording that makes it invalid to say friend virtual... The closest seems to be 7.1.2 dcl.fct.spec paragraph 5, which says:
The virtual specifier shall only be used in declarations of nonstatic class member functions that appear within a member-specification of a class definition; see 10.3 class.virtual.
I don't think that excludes a friend declaration (which is a valid member-specification by 9.2 class.mem).
John Spicer: I agree that virtual should not be allowed on friend declarations. I think the wording in 7.1.2 dcl.fct.spec is intended to be the declaration of a function within its class, although I think the wording should be improved to make it clearer.
Proposed resolution (October, 2005):
Change 7.1.2 dcl.fct.spec paragraphs 5-6 as indicated:
The virtual specifier shall
onlybe used only indeclarationsthe initial declaration of a non-static class memberfunctions that appear within a member-specification of a class definitionfunction; see10.3 class.virtual .The explicit specifier shall be used only in
declarationsthe declaration ofconstructorsa constructor withinaits class definition; see 12.3.1 class.conv.ctor.
7.1.5.2 dcl.type.simple paragraph 3 reads,
It is implementation-defined whether bit-fields and objects of char type are represented as signed or unsigned quantities. The signed specifier forces char objects and bit-fields to be signed; it is redundant with other integral types.
The last sentence in that quote is misleading w.r.t. bit-fields. The first sentence in that quote is correct but incomplete.
Proposed fix: change the two sentences to read:
It is implementation-defined whether objects of char type are represented as signed or unsigned quantities. The signed specifier forces char objects signed; it is redundant with other integral types except when declaring bit-fields (9.6 class.bit).
Proposed resolution (October, 2005):
Change 7.1.5.2 dcl.type.simple paragraph 3 as indicated:
When multiple simple-type-specifiers are allowed, they can be freely intermixed with other decl-specifiers in any order. [Note: It is implementation-defined whetherbit-fields andobjects of char type and certain bit-fields (9.6 class.bit) are represented as signed or unsigned quantities. The signed specifier forces bit-fields and char objectsand bit-fieldsto be signed; it is redundantwith other integral typesin other contexts. —end note]
As a result of the resolution of core issue 48, the current C++ standard is not in sync with existing practice and with user expectations as far as definitions of static data members having const integral or const enumeration type are concerned. Basically what current implementations do is to require a definition only if the address of the constant is taken. Example:
void f() {
std::string s;
...
// current implementations don't require a definition
if (s.find('a', 3) == std::string::npos) {
...
}
To the letter of the standard, though, the above requires a definition of npos, since the expression std::string::npos is potentially evaluated. I think this problem would be easily solved with simple changes to 9.4.2 class.static.data paragraph 4, 9.4.2 class.static.data paragraph 5 and 3.2 basic.def.odr paragraph 3.
Suggested resolution:
Replace 9.4.2 class.static.data paragraph 4 with:
If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be [note1] an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. No definition of the member is required, unless an lvalue expression that designates it is potentially evaluated and either used as operand to the built-in unary & operator [note 2] or directly bound to a reference.
If a definition exists, it shall be at namespace scope and shall not contain an initializer.
In 9.4.2 class.static.data paragraph 5 change
There shall be exactly one definition of a static data member that is used in a program; no diagnostic is required; see 3.2.
to
Except as allowed by 9.4.2 par. 4, there shall be exactly one definition of a static data member that is potentially evaluated (3.2) in a program; no diagnostic is required.
In 3.2 basic.def.odr paragraph 3 add, at the beginning:
Except for the omission allowed by 9.4.2, par. 4, ...
[note 1] Actually it shall be a "= followed by a constant-expression". This could probably be an editorial fix, rather than a separate DR.
[note 2] Note that this is the case when reinterpret_cast-ing to a reference, like in
struct X { static const int value = 0; };
const char & c = reinterpret_cast<const char&>(X::value);
See 5.2.10
expr.reinterpret.cast/10
More information, in response to a question about why issue 48 does not resolve the problem:
The problem is that the issue was settled in a way that solves much less than it was supposed to solve; that's why I decided to file, so to speak, a DR on a DR.
I understand this may seem a little 'audacious' on my part, but please keep reading. Quoting from the text of DR 48 (emphasis mine):
Originally, all static data members still had to be defined outside the class whether they were used or not.
But that restriction was supposed to be lifted [...]
In particular, if an integral/enum const static data member is initialized within the class, and its address is never taken, we agreed that no namespace-scope definition was required.
The corresponding resolution doesn't reflect this intent, with the definition being still required in most situations anyway: it's enough that the constant appears outside a place where constants are required (ignoring the obvious cases of sizeof and typeid) and you have to provide a definition. For instance:
struct X {
static const int c = 1;
};
void f(int n)
{
if (n == X::c) // <-- potentially evaluated
...
}
<start digression>
Most usages of non-enum BOOST_STATIC_COSTANTs, for instance, are (or were, last time I checked) non-conforming. If you recall, Paul Mensonides pointed out that the following template
// map_integral
template<class T, T V> struct map_integral : identity<T> {
static const T value = V;
};
template<class T, T V> const T map_integral<T, V>::value;
whose main goal is to map the same couples (type, value) to the same storage, also solves the definition problem. In this usage it is an excellent hack (if your compiler is good enough), but IMHO still a hack on a language defect.
<end digression>
What I propose is to solve the issue according to the original intent, which is also what users expect and all compilers that I know of already do. Or, in practice, we would have a rule that exists only as words in a standard document.
PS: I've sent a copy of this to Mr. Adamczyk to clarify an important doubt that occurred to me while writing this reply:
if no definition is provided for an integral static const data member is that member an object? Paragraph 1.8/1 seems to say no, and in fact it's difficult to think it is an object without assuming/pretending that a region of storage exists for it (an object *is* a region of storage according to the standard).
I would think that when no definition is required we have to assume that it could be a non-object. In that case there's nothing in 3.2 which says what 'used' means for such an entity and the current wording would thus be defective. Also, since the name of the member is an lvalue and 3.10/2 says an lvalue refers to an object we would have another problem.
OTOH the standard could pretend it is always an object (though the compiler can optimize it away) and in this case it should probably make a special case for it in 3.2/2.
Notes from the March 2004 meeting:
We sort of like this proposal, but we don't feel it has very high priority. We're not going to spend time discussing it, but if we get drafting for wording we'll review it.
Proposed resolution (October, 2005):
Change the first two sentences of 3.2 basic.def.odr paragraph 2 from:
An expression is potentially evaluated unless it appears where an integral constant expression is required (see 5.19 expr.const), is the operand of the sizeof operator (5.3.3 expr.sizeof), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (5.2.8 expr.typeid). An object or non-overloaded function is used if its name appears in a potentially-evaluated expression.
to
An expression that is the operand of the sizeof operator (5.3.3 expr.sizeof) is unevaluated, as is an expression that is the operand of the typeid operator if it is not an lvalue of a polymorphic class type (5.2.8 expr.typeid); all other expressions are potentially evaluated. An object or non-overloaded function whose name appears as a potentially-evaluated expression is used, unless it is an object that satisfies the requirements for appearing in an integral constant expression (5.19 expr.const) and the lvalue-to-rvalue conversion (4.1 conv.lval) is immediately applied.
Change the first sentence of 9.4.2 class.static.data paragraph 2 as indicated:
If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializerwhichwhose constant-expression shall be an integral constant expression (5.19 expr.const).
Section 9.6 class.bit paragraph 4 needs to be more specific about the signedness of bit fields of enum type. How much leeway does an implementation have in choosing the signedness of a bit field? In particular, does the phrase "large enough to hold all the values of the enumeration" mean "the implementation decides on the signedness, and then we see whether all the values will fit in the bit field", or does it require the implementation to make the bit field signed or unsigned if that's what it takes to make it "large enough"?
(See also issue 172.)
Note (March, 2005): Clark Nelson observed that there is variation among implementations on this point.
Notes from April, 2005 meeting:
Although implementations enjoy a great deal of latitude in handling bit-fields, it was deemed more user-friendly to ensure that the example in paragraph 4 will work by requiring implementations to use an unsigned underlying type if the enumeration type has no negative values. (If the implementation is allowed to choose a signed representation for such bit-fields, the comparison against TRUE will be false.)
In addition, it was observed that there is an apparent circularity between 7.2 dcl.enum paragraph 7 and 9.6 class.bit paragraph 4 that should be resolved.
Proposed resolution (April, 2006):
Replace 7.2 dcl.enum paragraph 7, deleting the embedded footnote 85, with the following:
For an enumeration where emin is the smallest enumerator and emax is the largest, the values of the enumeration are the values in the range bmin to bmax, defined as follows: Let K be 1 for a two's complement representation and 0 for a one's complement or sign-magnitude representation. bmax is the smallest value greater than or equal to max(|emin|-K,|emax|) and equal to 2M-1, where M is a non-negative integer. bmin is zero if emin is non-negative and -(bmax+K) otherwise. The size of the smallest bit-field large enough to hold all the values of the enumeration type is max(M,1) if bmin is zero and M+1 otherwise. It is possible to define an enumeration that has values not defined by any of its enumerators.
Add the indicated text to the second sentence of 9.6 class.bit paragraph 4:
If the value of an enumerator is stored into a bit-field of the same enumeration type and the number of bits in the bit-field is large enough to hold all the values of that enumeration type (7.2 dcl.enum), the original enumerator value and the value of the bit-field shall compare equal.
Issue 298, recently approved, affirms that cv-qualified class types can be used as nested-name-specifiers. Should the same be true for base-specifiers?
Rationale (April, 2005):
The resolution of issue 298 added new text to 9.1 class.name paragraph 5 making it clear that a typedef that names a cv-qualified class type is a class-name. Because the definition of base-specifier simply refers to class-name, it is already the case that cv-qualified class types are permitted as base-specifiers.
Additional notes (June, 2005):
It's not completely clear what it means to have a cv-qualified type as a base-specifier. The original proposed resolution for issue 298 said that “the cv-qualifiers are ignored,” but that wording is not in the resolution that was ultimately approved.
If the cv-qualifiers are not ignored, does that mean that the base-class subobject should be treated as always similarly cv-qualified, regardless of the cv-qualification of the derived-class lvalue used to access the base-class subobject? For instance:
typedef struct B {
void f();
void f() const;
int i;
} const CB;
struct D: CB { };
void g(D* dp) {
dp->f(); // which B::f?
dp->i = 3; // permitted?
}
Proposed resolution (October, 2005):
Change 9.1 class.name paragraph 5 as indicated:
A typedef-name (7.1.3 dcl.typedef) that names a class type, or a cv-qualified version thereof, is also aclass-name, butclass-name. If a typedef-name that names a cv-qualified class type is used where a class-name is required, the cv-qualifiers are ignored. A typedef-name shall not be used as the identifier in a class-head.
Delete 7.1.3 dcl.typedef paragraph 8:
[Note: if the typedef-name is used where a class-name (or enum-name) is required, the program is ill-formed. For example,
typedef struct { S(); // error: requires a return type because S is // an ordinary member function, not a constructor } S;—end note]
The proposed resolution for issue 45 inserts the following sentence after 11 class.access paragraph 1:
A member of a class can also access all names as the class of which it is a member.
I don't think that this is correctly constructed English. I see two possibilities:
This is a typo, and the correct change is:
A member of a class can also access all names of the class of which it is a member.
The intent is something more like:
A member of a nested class can also access all names accessible by any other member of the class of which it is a member.
[Note: this was editorially corrected at the time defect resolutions were being incorporated into the Working Paper to read, “...can also access all the names declared in the class of which it is a member,” which is essentially the same as the preceding option 1.]
I would prefer to use the language proposed for 11.8 class.access.nest:
A nested class is a member and as such has the same access rights as any other member.
A second problem is with the text in 11.4 class.friend paragraph 2:
[Note: this means that access to private and protected
names is also granted to member functions of the friend class (as
if the functions were each friends) and to the static data member
definitions of the friend class. This also means that private and
protected type names from the class granting friendship can be
used in the base-clause of a nested class of the friend
class. However, the declarations of members of classes nested
within the friend class cannot access the names of private and
protected members from the class granting friendship. Also,
because the base-clause of the friend class is not part of
its member declarations, the base-clause of the friend
class cannot access the names of the private and protected
members from the class granting friendship. For example,
class A {
class B { };
friend class X;
};
class X : A::B { // ill-formed: A::B cannot be accessed
// in the base-clause for X
A::B mx; // OK: A::B used to declare member of X
class Y: A::B { // OK: A::B used to declare member of X
A::B my; // ill-formed: A::B cannot be accessed
// to declare members of nested class of X
};
};
—end note]
This seems to be an oversight. The proposed change to 11.8 class.access.nest paragraph 1 would appear to have eliminated the restrictions on nested class access. However, at least one compiler (gcc 3.4.3) doesn't appear to take my view, and continues with the restrictions on access by classes within a friend class, while implementing the rest of the resolution of issue 45.
Note (March, 2005):
Andreas Hommel: I think issue 45 requires an additional change in 9.7 class.nest paragraph 4:
Like a member function, a friend function (11.4 class.friend) defined within a nested class is in the lexical scope of that class; it obeys the same rules for name binding as a static member function of that class (9.4 class.static) and has no special access rights to members of an enclosing class.
I believe the “no special access rights” language should be removed.
Proposed resolution (October, 2005):
This issue is resolved by the resolution of issue 372.
I don't know the reason for this distinction, but it seems to be surprising that Base::A is legal and D is illegal in this example:
class D;
class Base
{
class A;
class B;
friend class D;
};
class Base::B
{
};
class Base::A : public Base::B // OK because of issue 45
{
};
class D : public Base::B // illegal because of 11.4p4
{
};
Shouldn't this be consistent (either way)?
Notes from the April, 2005 meeting:
In discussing issue 372, the CWG decided that access in the base-specifiers of a class should be the same as for its members, and that resolution will apply to friend declarations, as well.
Proposed resolution (October, 2005):
This issue is resolved by the resolution of issue 372.
Taken literally, 14 temp paragraph 2 does not permit operator functions to be templates:
In a function template declaration, the declarator-id shall be a template-name (i.e., not a template-id).
and, in
Issue 301 considered and rejected the idea of changing the definition of template-name to include operator-function-ids and conversion-function-ids. Either that decision should be reconsidered or the various references in the text to template-name should be examined to determine if they should also mention the non-identifier possibilities for function template names.
Proposed resolution (April, 2006):
This issue is resolved by the resolution of issue 301.
The grammar for a template-name is:
That's not right; consider:
template <class T> T operator+(const T&, const T&);
template <> S operator+<S>(const S&, const S&);
This is ill-formed according to the standard, since operator+ is not a template-name.
Suggested resolution:
I think the right rule is
John Spicer adds that there's some question about whether conversion functions should be included, as they cannot have template argument lists.
Notes from 4/02 meeting:
If the change is made as a syntax change, we'll need a semantic restriction to avoid operator+<int> as a class. Clark Nelson will work on a compromise proposal -- not the minimal change to the syntax proposed, not the maximal change either.
Clark Nelson (April 2003):
The proposed solution (adding operator-function-id as an alternative for template-name) would have a large impact on the language described by the grammar. Specifically, for example, operator+<int> would become a syntactically valid class-name.
On the other hand, a change with (I believe) exactly the desired effect on the language accepted, would be to modify operator-function-id itself:
(Steve Adamczyk: this change was already made by issue 38 and is in TC1.)
Then there is the first sentence of 14.2 temp.names paragraph 3:
After name lookup (3.4 basic.lookup) finds that a name is a template-name, if this name is followed by a <, the < is always taken as the beginning of a template-argument-list and never as a name followed by the less-than operator.
This description seems to be adequate for names of class templates. As far as I can tell, the only ambiguity it resolves is from something that starts with new X <, in the scope of a class template X. But as far as I can tell is already inadequate for names of function templates, and is even worse for operator function templates.
Probably < should always be interpreted as introducing a template-argument-list if any member of the overload set is a function template. After all, function pointers are very rarely compared for ordering, and it's not clear what other rule might be workable.
I'm inclined to propose the simplest rule possible for operator-function-ids: if one is followed by <, then what follows is interpreted as a template-argument-list, unconditionally. Of course, if no template for that operator has been declared, then there's an error.
Also, note that if the operator in question is < or <<, it is possible to run into a problem similar to the famous >> nested template argument list closing delimiter problem. However, since in this case (at least) one of the < characters has a radically different interpretation than the other, and for other reasons as well, this is unlikely to be nearly as much of a practical problem as the >> problem.
Notes from April 2003 meeting:
We felt that the operator functions should not be special-cased. They should be treated like any other name.
September 2003:
Clark Nelson has provided the changes in N1490=03-0073.
Notes from October 2003 meeting:
We reviewed Clark Nelson's N1490. Clark will revise it and introduce a new syntax term for an identifier or the name of an operator function.
Notes from the April, 2005 meeting:
The CWG suggested a new approach to resolving this issue: the existing term template-id will be renamed to class-template-id, the term template-id will be defined to include operator functions with template arguments, and any current uses of template-id (such as in the definition of elaborated-type-specifier) where an operator function is not appropriate will be changed to refer to class-template-id.
Proposed resolution (April, 2006):
As specified in document J16/05-0156 = WG21 N1896, except that:
In change 9 (3.4.5 basic.lookup.classref), omit the change from “entire postfix-expression” to “nested-name-specifier.”
In change a (3.4.3.1 class.qual paragraph 1, third bullet), omit the change from “entire postfix-expression” to “qualified-id.”
In change b (3.4.3.2 namespace.qual paragraph 1), omit the change from “entire postfix-expression” to “qualified-id.”
(Some of these omitted changes are addressed by issue 562.)
(This resolution also resolves issue 534.)
For the same reasons that issue 382 proposes for relaxation of the requirements on typename, it would make sense to allow the ::template disambiguator outside of templates.
See also issues 11, 30, 96, and 109.
Proposed resolution (October, 2005):
Change 14.2 temp.names paragraph 5 as indicated:
If a name prefixed by the keyword template is not the name of a template, the program is ill-formed. [Note: the keyword template may not be applied to non-template members of class templates. —end note]Furthermore, names of member templates shall not be prefixed by the keyword template if the postfix-expression or qualified-id does not appear in the scope of a template.[Note: just as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the nested-name-specifier or the expression on the left of the -> or ., or the nested-name-specifieris not dependent on a template-parameter, or the use does not appear in the scope of a template. —end note]
I'm not really sure what the standard says about this. Personally, I'd like for it to be ill-formed, but I can't find any words that I can interpret to say so.
template<class T>
class X
{
protected:
typedef T Type;
};
template<class T>
class Y
{
};
template<class T,
template<class> class T1,
template<class> class T2>
class Z:
public T2<typename T1<T>::Type>,
public T1<T>
{
};
Z<int, X, Y> z;
John Spicer: I don't think the standard really addresses this case. There is wording about access checking of things used as template arguments, but that doesn't address accessing members of the template argument type (or template) from within the template.
This example is similar, but does not use template template arguments.
class X {
private:
struct Type {};
};
template <class T> struct A {
typename T::Type t;
};
A<X> ax;
This gets an error from most compilers, though the standard is probably mute on this as well. An error makes sense -- if there is no error, there is a hole in the access checking. (The special rule about no access checks on template parameters is not a hole, because the access is checked on the type passed in as an argument. But when you look up something in the scope of a template parameter type, you need to check the access to the member found.)
The logic in the template template parameter case should be similar: anytime you look up something in a template-dependent class, the member's access must be checked, because it could be different for different template instances.
Proposed Resolution (October 2002):
Change the last sentence of 14.3 temp.arg paragraph 3 from:
For a template-argument of class type, the template definition has no special access rights to the inaccessible members of the template argument type.to:
For a template-argument that is a class type or a class template, the
template definition has no special access rights to the members
of the template-argument. [Example:
template <template <class TT> class T> class A {
typename T<int>::S s;
};
template <class U> class B {
private:
struct S { /* ... */ };
};
A<B> b; // ill-formed, A has no access to B::S
-- end example]
Daniel Frey posts on comp.std.c++ in July 2003: I just read DR 372 and I think that the problem presented is not really discussed/solved properly. Consider this example:
class A {
protected:
typedef int N;
};
template< typename T >
class B
{};
template< typename U >
class C : public U, public B< typename U::N >
{};
C< A > x;
The question is: If C is derived from A as above, is it allowed to access A::N before the classes opening '{'?
The main problem is that you need to access U's protected parts in C's base-clause. This pattern is common when using policies, Andrei's Loki library was bitten by it as he tried to make some parts of the policies 'protected' but some compilers rejected the code. They were right to reject it, I think it's 11.4 class.friend/2 that applies here and prevents the code above to be legal, although it addresses a different and reasonable example. To me, it seems wrong to reject the code as it is perfectly reasonable to write such stuff. The questions are:
Steve Adamczyk: In other words, the point of the issue is over what range access derived from base class specifiers is granted, and whether any part of that range is the base specifier list itself, either the parts afterwards or the whole base specifier list. (Clark Nelson confirms this is what he was asking with the original question.) Personally, I find it somewhat disturbing that access might arrive incrementally; I'd prefer that the access happen all at once, at the opening brace of the class.
Notes from October 2003 meeting:
We decided it makes sense to delay the access checking for the base class specifiers until the opening brace of the class is seen. In other words, the base specifiers will be checked using the full access available for the class, and the order of the base classes is not significant in that determination. The implementors present all said they already had code to handle accumulation of delayed access checks, because it is already needed in other contexts.
Proposed resolution (October, 2004):
Change the last sentence of 14.3 temp.arg paragraph 3 as indicated:
For a template-argumentofthat is a class type or a class template, the template definition has no special access rights to theinaccessiblemembers of thetemplate argument typetemplate-argument. [Example:template <template <class TT> class T> class A { typename T<int>::S s; }; template <class U> class B { private: struct S { /* ... */ }; }; A<B> b; // ill-formed, A has no access to B::S—end example]
Insert the following as a new paragraph after the final paragraph of 11 class.access:
The access of names in a class base-specifier-list are checked at the end of the list after all base classes are known. [Example:
class A { protected: typedef int N; }; template<typename T> class B {}; template<typename U> class C : public B<typename U::N>, public U {}; C<A> x; // OK: A is a base class so A::N in B<A::N> is accessible
—end example]
Notes from the April, 2005 meeting:
The 10/2004 resolution is not sufficient to implement the CWG's intent to allow these examples: clause 11 class.access paragraph 1 grants protected access only to “members and friends” of derived classes, not to their base-specifiers. The resolution needs to be extended to say either that access in base-specifiers is determined as if they were members of the class being defined or that access is granted to the class as an entity, including its base-specifiers. See also issue 500, which touches on the same issue and should be resolved in the same way.
Proposed resolution (October, 2005):
Change the second bullet of 11 class.access paragraph 1 as indicated:
protected; that is, its name can be used only by
members and friends of the class in which it is declared, and by
members and friends of classes derived from this class by
classes derived from that class, and by their friends (see
11.5
class.protected).
Change 11 class.access paragraph 2 as indicated:
A member of a class can also access all the namesdeclared in the class of which it is a memberto which the class has access.
Change 11 class.access paragraph 6 as indicated:
All access controls in clause 11 class.access affect the ability to access a class member name from a particular scope.The access control for names used in the definition of a class member that appears outside of the member's class definition is done as if the entire member definition appeared in the scope of the member's class.For purposes of access control, the base-specifiers of a class and the definitions of class members that appear outside of the class definition are considered to be within the scope of that class. In particular...
Change the example and commentary in 11 class.access paragraphs 6-7 as indicated:
[Example:
class A { typedef int I; // private member I f(); friend I g(I); static I x; protected: struct B { }; }; A::I A::f () { return 0; } A::I g(A::I p = A::x); A::I g(A::I p) { return 0; } A::I A::x = 0; struct D: A::B, A { };Here, all the uses of A::I are well-formed because A::f and A::x are members of class A and g is a friend of class A. This implies, for example, that access checking on the first use of A::I must be deferred until it is determined that this use of A::I is as the return type of a member of class A. Similarly, the use of A::B as a base-specifier is well-formed because D is derived from A, so access checking of base-specifiers must be deferred until the entire base-specifier-list has been seen. —end example]
In 11.4 class.friend paragraph 2, replace the following text:
Declaring a class to be a friend implies that the names of private and protected members from the class granting friendship can be accessed in declarations of members of the befriended class. [Note: this means that access to private and protected names is also granted to member functions of the friend class (as if the functions were each friends) and to the static data member definitions of the friend class. This also means that private and protected type names from the class granting friendship can be used in the base-clause of a nested class of the friend class. However, the declarations of members of classes nested within the friend class cannot access the names of private and protected members from the class granting friendship. Also, because the base-clause of the friend class is not part of its member declarations, the base-clause of the friend class cannot access the names of the private and protected members from the class granting friendship. For example,
class A { class B { }; friend class X; }; class X: A::B { // ill-formed: A::B cannot be accessed // in the base-clause for X A::B mx; // OK: A::B used to declare member of X class Y: A::B { // OK: A::B used to declare member of X A::B my; // ill-formed: A::B cannot be accessed // to declare members of nested class of X }; };—end note]
with:
Declaring a class to be a friend implies that the names of private and protected members from the class granting friendship can be accessed in the base-specifiers and member declarations of the befriended class. [Example:
class A { class B { }; friend class X; }; struct X: A::B { // OK: A::B accessible to friend A::B mx; // OK: A::B accessible to member of friend class Y { A::B my; // OK: A::B accessible to nested member of friend }; };—end example]
Change the last sentence of 14.3 temp.arg paragraph 3 as indicated:
For a template-argument
ofthat is a class type or a class template, the template definition has no special access rights to theinaccessiblemembers of thetemplate argument type.template-argument. [Example:template <template <class TT> class T> class A { typename T<int>::S s; }; template <class U> class B { private: struct S { /* ... */ }; }; A<B> b; // ill-formed, A has no access to B::S—end example]
Change 9.7 class.nest paragraph 4 as indicated:
Like a member function, a friend function (11.4 class.friend) defined within a nested class is in the lexical scope of that class; it obeys the same rules for name binding as a static member function of that class (9.4 class.static), but itandhas no special access rights to members of an enclosing class.
(Note: this resolution also resolves issues 494 and 500.)
According to 14.5.4 temp.class.spec paragraph 1,
If a template is partially specialized then that partial specialization shall be declared before the first use of that partial specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.
This leaves the impression that an explicit instantiation of the primary template may precede the declaration of an applicable partial specialization. Is the following example well-formed?
template<typename T> class X{
public:
void foo(){};
};
template class X<void *>;
template<typename T> class X<T*>{
public:
void baz();
};
void bar() {
X<void *> x;
x.foo();
}
Proposed resolution (October, 2005):
Replace the last sentence of 14.5.4 temp.class.spec paragraph 1:
If a template is partially specialized then that partial specialization shall be declared before the first use of that partial specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.
with:
A partial specialization shall be declared before the first use of a class template specialization that would make use of the partial specialization as the result of an implicit or explicit instantiation in every translation unit in which such a use occurs; no diagnostic is required.
Implementations vary in their treatment of the following code:
struct A {
int foo_;
};
template <typename T> struct B: public A { };
template <typename T> struct C: B<T> {
int foo() {
return A::foo_; // #1
}
};
int f(C<int>* p) {
return p->foo();
}
According to one analysis, because the expression A::foo_ on line #1 is non-dependent, it must be analyzed in the definition context. It that context, it violates the restrictions of 9.2 class.mem paragraph 10 on how the name of a nonstatic data member of a class can be used and thus should be treated as an error.
On the other hand, the description of the transformation of an id-expression into a class member access expression (9.3.1 class.mfct.nonstatic paragraph 3) does not have any special treatment of templates; when C<int>::foo() is instantiated, the reference to A::foo_ turns out to be to a base class member and is thus transformed into (*this).A::foo_ and is thus not an error.
Proposed resolution (October, 2005):
Change 9.3.1 class.mfct.nonstatic paragraph 3 as indicated:
When an id-expression (5.1 expr.prim) that is not part of a class member access syntax (5.2.5 expr.ref) and not used to form a pointer to member (5.3.1 expr.unary.op) is used in the body of a non-static member function of class X or used in the mem-initializer for a constructor of class X, if name lookup (3.4.1 basic.lookup.unqual) resolves the name in the id-expression to a non-static non-type member ofclass X or of a base class of Xsome class C, the id-expression is transformed into a class member access expression (5.2.5 expr.ref) using (*this) (9.3.2 class.this) as the postfix-expression to the left of the . operator. [Note: If C is not X or a base class of X, the class member access expression is ill-formed. —end note]The member name then refers to the member of the object for which the function is called.Similarly during name lookup...
The description of dependent function calls in 14.6.2 temp.dep paragraph 1 applies only to identifiers in postfix-notation function calls and to operator notation calls for operator functions:
In an expression of the form:
postfix-expression ( expression-listopt )
where the postfix-expression is an identifier, the identifier denotes a dependent name if and only if any of the expressions in the expression-list is a type-dependent expression (14.6.2.2 temp.dep.expr). If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name.
It would appear from the related passage in 14.6.4.2 temp.dep.candidate paragraph 1 that the description of postfix-notation function calls should apply to all unqualified-ids that are not template-ids, including operator-function-ids, not just to identifiers:
For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found...
Proposed resolution (October, 2005):
Change 14.6.2 temp.dep paragraph 1 as indicated:
...In an expression of the form:
postfix-expression ( expression-listopt )
where the postfix-expression is an
identifierunqualified-id but not a template-id, theidentifierunqualified-id denotes a dependent name if and only if any of the expressions in the expression-list is a type-dependent expression (14.6.2.2 temp.dep.expr)...
Change 14.6.4.2 temp.dep.candidate paragraph 1 as indicated:
For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, or if the function is called using operator notation, the candidate functions are found using the usual lookup rules (3.4.1 basic.lookup.unqual, 3.4.2 basic.lookup.argdep) except that...
(See also issue 561.)
Consider the following example:
char* cmdline3_[1] = {};
template<class charT>
void func(const charT* const argv[]) {}
int main()
{
func(cmdline3_);
}
In terms of the process described in 14.8.2.1 temp.deduct.call, P is const charT* const * and A is char*[1]. According to the first bullet in paragraph 2, the type used in deduction is not A but “the pointer type produced by the array-to-pointer standard conversion.”
According to paragraph 4,
In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:
In this example, the deduced A is not identical to the transformed A, because the deduced A has additional cv-qualification, so the three exceptions must be examined to see if they apply. The only one that might apply is the second bullet of paragraph 4:
- A can be another pointer or pointer to member type that can be converted to the deduced A via a qualification conversion (4.4 conv.qual).
However, A is not a pointer type but an array type; this provision does not apply and deduction fails.
It has been argued that the phrase “after the type A is transformed as described above” should be understood to apply to the A in the three bullets of paragraph 4. If that is the intent, the wording should be changed to make that explicit.
Proposed resolution (October, 2005):
Add the indicated words to 14.8.2 temp.deduct paragraph 4:
In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:
If the original P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than the transformed A.
The transformed A can be another pointer or pointer to member type that can be converted to the deduced A via a qualification conversion (4.4 conv.qual).
If P is a class, and P has the form template-id, then the transformed A can be a derived class of the deduced A. Likewise, if P is a pointer to a class of the form template-id, the transformed A can be a pointer to a derived class pointed to by the deduced A.
14.8.2.5 temp.deduct.type paragraph 5 reads:
The non-deduced contexts are:
The nested-name-specifier of a type that was specified using a qualified-id.
A non-type template argument or an array bound that is an expression that references a template parameter.
A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
A function parameter for which argument deduction cannot be done because the associated function argument is a function, or a set of overloaded functions (13.4 over.over), and one or more of the following apply:
more than one function matches the function parameter type (resulting in an ambiguous deduction), or
no function matches the function parameter type, or
the set of functions supplied as an argument contains one or more function templates.
An array bound that is an expression that references a template-parameter.
There are two problems with this list:
The last bullet is redundant with the second bullet. This appears to have been the result of applying the resolutions of issues 70 and 352 independently instead of in coordination.
The second bullet appears to be contradicted by the statement in paragraph 8 saying that an argument can be deduced if P and A have the forms type[i] and template-name<i>.
The intent of the wording in bullet 2 appears to have been that deduction cannot be done if the template parameter is a sub-expression of the template argument or array bound expression and that it can be done if it is the complete expression, but the current wording does not say that very clearly. (Similar wording also appears in 14.6.2.1 temp.dep.type paragraph 3 and 14.8.2.5 temp.deduct.type paragraph 14.)
Proposed resolution (October, 2005):
Change 14.8.2.5 temp.deduct.type paragraph 5 as indicated:
The non-deduced contexts are:
The nested-name-specifier of a type that was specified using a qualified-id.
A non-type template argument or an array bound
that is an expression thatin either of which a subexpression references a template parameter.A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
A function parameter for which argument deduction cannot be done because the associated function argument is a function, or a set of overloaded functions (13.4 over.over), and one or more of the following apply:
more than one function matches the function parameter type (resulting in an ambiguous deduction), or
no function matches the function parameter type, or
the set of functions supplied as an argument contains one or more function templates.
An array bound that is an expression that references a template-parameter.
Change 14.8.2.5 temp.deduct.type paragraph 14 as indicated:
If, in the declaration of a function template with a non-type template parameter, the non-type template parameter is used inan expressiona subexpression in the function parameter list, the expression is a non-deduced context as specified above...
Change 14.6.2.1 temp.dep.type paragraph 3 as indicated:
A template argument that is equivalent to a template parameter (i.e., has the same constant value or the same type as the template parameter) can be used in place of that template parameter in a reference to the current instantiation. In the case of a non-type template argument, the argument must have been given the value of the template parameter and not an expressioninvolvingthat contains the template parameter as a subexpression...
Mike Miller: In fact, now that I've looked more closely, that appears not to be the case. (At least, it's not the error I get when I compile his example.) Here's a minimal extract (without the inflammatory using-directive :-) that illustrates what I think is going on:
template <typename _Iterator>
struct iterator_traits {
typedef typename _Iterator::difference_type difference_type;
};
template <typename _InputIterator>
inline typename iterator_traits<_InputIterator>::difference_type
distance(_InputIterator, _InputIterator);
double distance(const int&, const int&);
void f() {
int i = 0;
int j = 0;
double d = distance(i, j);
}
What happens is that iterator_traits<int> is instantiated as part of type deduction for the function template distance, and the instantiation fails. (Note that it can't be instantiation of distance<int>, as I had originally posited, because in this case only a declaration, not a definition, of that template is in scope.)
John Spicer: Yes, I believe that is what is going on.
Mike Miller: I seem to recall that there was some discussion of questions related to this during the core meetings in Oxford. I think Steve Adamczyk said something to the effect that it's infeasible to suppress all instantiation errors during template type deduction and simply call any such errors a deduction failure. (I could be misremembering, and I could be misapplying that comment to this situation.)
John Spicer: Regardless of other conditions in which this may apply, I don't think it would be reasonable for compilers to have to do "speculative instantiations" during template argument deduction. One class instantiation could kick off a series of other instantiations, etc.
Mike Miller: I don't see anything in the Standard that tells me whether it's legitimate or not to report an error in this case. I hope John or another template expert can enlighten me on that.
John Spicer: My opinion is that, because this case is not among those enumerated that cause deduction failure (rather than being ill-formed) that reporting an error is the right thing to do.
Mike Miller: I am still interested, though, in the question of why 14.8.3 temp.over says that viable function template specializations are instantiated, even if they are not selected by overload resolution.
John Spicer: I believe the wording in 14.8.3 temp.over is incorrect. I researched this and found that a change was made during the clause 14 restructuring that was incorporated in March of 1996. The prior wording was "the deduced template arguments are used to generate a single template function". This was changed to "deduced template arguments are used to instantiate a single function template specialization". I believe this resulted from what was basically a global replace of "generate" with "instantiate" and of "template function" with "function template specialization". In this case, the substitution changed the meaning. This paragraph needs reworking.
Proposed resolution (April, 2006):
Change 14.8.3 temp.over paragraph 1 as indicated:
...For each function template, if the argument deduction and checking succeeds, the template-arguments (deduced and/or explicit) are used toinstantiatesynthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all thefunction templates instantiated in this waysynthesized declarations and all of the non-template overloaded functions of the same name. Thefunction template specializationssynthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in 13.3.3 over.match.best.
The motivation for this issue is a desire to write portable programs which will work with any conforming implementation.
The C++ Standard (16.2 cpp.include) provides two forms of #include directives, with the <...> form being described (16.2 cpp.include paragraph 2) as "for a header", and the "..." form (16.2 cpp.include paragraph 3) as for "the source file" identified between the delimiters. When the standard uses the term "header", it often appears to be limiting the term to apply to the Standard Library headers only. Users of the standard almost always use the term "header" more broadly, to cover all #included source files, but particularly those containing interface declarations.
Headers, including source files, can be categorized according to their origin and usage:
Existing practice varies widely, but it is fairly easy to find users advocating:
Do any of the practices A, B, or C result in programs which can be rejected by a conforming implementation?
The first defect is that readers of the standard have not been able to reach consensus on the answers to the above question.
A second possible defect is that if A, B, or C can be rejected by a conforming implementation, then the standard should be changed because would mean there is a wide variance between the standard and existing practice.
Matt Austern: I really only see two positions:
I agree that the standard should clarify which of those two is the case (I imagine it'll hinge on finding one crucual sentence that either says "implementation defined" or "unspecified"), but from the standpoint of portability I don't see much difference between the two. I claim that, with either of those two interpretations, using #include <foo> is already nonportable.
(Of course, I claim that almost anything having to do with headers, including the #include "foo" form, is also nonportable. In practice there's wide variation in how compilers handle paths, especially relative paths.)
Beman Dawes: The whole issue can be resolved by replacing "header" with "header or source file" in 16.2 cpp.include paragraph 2. That will bring the standard into alignment with existing practice by both users and implementations. The "header and/or source file" wording is used at least three other places in the standard where otherwise there might be some confusion.
John Skaller: In light of Andrew Koenig's comments, this doesn't appear to be the case, since the mapping of include names to file names is implementation defined, and therefore source file inclusion cannot be made portable within the ISO C/C++ standards (since that provision obviously cannot be removed).
A possible idea is to create a binding standard, outside the C/C++ ISO Standards, which specifies not only the path lookup mechanism but also the translation from include names to file names. Clearly that is OS dependent, encoding dependent, etc, but there is no reason not to have a binding standard for Unix, Windows, etc, and specify these bindings in such a way that copying directories from one OS to the other can result in programs working on both OS's.
Andy Koenig: An easier solution might be to specify a (presumably unbounded, or bounded only by implementation capacit