| Document number: | PL22.16/09-0069 = WG21 N2879 |
| Date: | 2009-06-19 |
| 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 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.
Section references in this document reflect the section numbering of document PL22.16/09-0047 = WG21 N2857.
The standard is inconsistent in its use of a hyphen on the following: nontype vs. non-type, non-dependent vs. nondependent, non-deduced vs. nondeduced, and non-template vs. nontemplate. We should pick a preferred form.
Notes from the March 2004 meeting:
If this isn't a purely editorial issue, nothing is. We're referring this to the editor. We prefer the hyphenated forms.
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.
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.7
[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.
The wording of 3.4.1 [basic.lookup.unqual] paragraph 2 is misleading. It says:
The declarations from the namespace nominated by a using-directive become visible in a namespace enclosing the using-directive; see 7.3.4 [namespace.udir].
According to 7.3.4 [namespace.udir] paragraph 1, that namespace is
the nearest enclosing namespace which contains both the using-directive and the nominated namespace.
That would seem to imply the following:
namespace outer {
namespace inner {
int i;
}
void f() {
using namespace inner;
}
int j = i; // inner::i is "visible" in namespace outer
}
Suggested resolution: Change the first sentence of 3.4.1 [basic.lookup.unqual] paragraph 2 to read:
The declarations from the namespace nominated by a using-directive become visible in the scope in which the using-directive appears after the using-directive.
Notes from the 4/02 meeting:
After a lot of discussion of possible wording changes, we decided the wording should be left alone. 3.4.1 [basic.lookup.unqual] paragraph 2 is not intended to be a full specification; that's in 7.3.4 [namespace.udir] paragraph 1. See also 3.3.6 [basic.scope.namespace] paragraph 1.
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.
I believe the following code example should unambiguously call the member operator+. Am I right?
//--- some library header ---
//
namespace N1 {
template<class T> struct Base { };
template<class T> struct X {
struct Y : public Base<T> { // here's a member operator+
Y operator+( int _Off ) const { return Y(); }
};
Y f( unsigned i ) { return Y() + i; } // the "+" in question
};
}
//--- some user code ---
//
namespace N2 {
struct Z { };
template<typename T> // here's another operator+
int* operator+( T , unsigned ) { static int i ; return &i ; }
}
int main() {
N1::X< N2::Z > v;
v.f( 0 );
}
My expectation is that 3.4.2 [basic.lookup.argdep] would govern, specifically:
If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered.So I think the member should hide the otherwise-better-matching one in the associated namespace. Here's what compilers do:
Agree with me and call the member operator+: Borland 5.5, Comeau 4.3.0.1, EDG 3.0.1, Metrowerks 8.0, MSVC 6.0
Disagree with me and try to call N2::operator+: gcc 2.95.3, 3.1.1, and 3.2; MSVC 7.0
Simple so far, but someone tells me that 13.3.1.2 [over.match.oper] muddies the waters. There, paragraph 10 summarizes that subclause:
[Note: the lookup rules for operators in expressions are different than the lookup rules for operator function names in a function call, ...In particular, consider the above call to "Y() + unsigned" and please help me step through 13.3.1.2 [over.match.oper] paragraph 3:
... for a binary operator @ with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type whose cv-unqualified version is T2,OK so far, here @ is +, and T1 is N1::X::Y.
three sets of candidate functions, designated member candidates, non-member candidates and built-in candidates, are constructed as follows:[and later are union'd together to get the candidate list]
If T1 is a class type, the set of member candidates is the result of the qualified lookup of T1::operator@ (over.call.func); otherwise, the set of member candidates is empty.So there is one member candidate, N1::X::Y::operator+.
The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual rules for name lookup in unqualified function calls (basic.lookup.argdep) except that all member functions are ignored.
*** This is the question: What does that last phrase mean? Does it mean:
a) first apply the usual ADL rules to generate a candidate list, then ignore any member functions in that list (this is what I believe and hope it means, and in particular it means that the presence of a member will suppress names that ADL would otherwise find in the associated namespaces); or
b) something else?
In short, does N2::operator+ make it into the candidate list? I think it shouldn't. Am I right?
John Spicer: I believe that the answer is sort-of "a" above. More specifically, the unqualified lookup consists of a "normal" unqualified lookup and ADL. ADL always deals with only namespace members, so the "ignore members functions" part must affect the normal lookup, which should ignore class members when searching for an operator.
I suspect that the difference between compilers may have to do with details of argument-dependent lookup. In the example given, the argument types are "N1::X<N2::Z>::Y" and "unsigned int". In order for N2::operator+ to be a candidate, N2 must be an associated namespace.
N1::X<N2::Z>::Y is a class type, so 3.4.2 [basic.lookup.argdep] says that its associated classes are its direct and indirect base classes, and its namespaces are the namespaces of those classes. So, its associated namespace is just N1.
3.4.2 [basic.lookup.argdep] also says:
If T is a template-id, 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. ]First of all, there is a problem with the term "is a template-id". template-id is a syntactic constuct and you can't really talk about a type being a template-id. Presumably, this is intended to mean "If T is the type of a class template specialization ...". But does this apply to N1::X<N2::Z>::Y? Y is a class nested within a class template specialization. In addition, its base class is a class template specialization.
I think this raises two issues:
Notes from the April 2003 meeting:
The ADL rules in the standard sort of look at if they are fully recursive, but in fact they are not; in some cases, enclosing classes and base classes are considered, and in others they are not. Microsoft and g++ did fully-recursive implementations, and EDG and IBM did it the other way. Jon Caves reports that Microsoft saw no noticeable difference (e.g., no complaints from customers internal or external) when they made this change, so we believe that even if the rules are imperfect the way they are in the standard, they are clear and the imperfections are small enough that programmers will not notice them. Given that, it seemed prudent to make no changes and just close this issue.
The template-id issue is spun off as issue 403.
There is a discrepancy between the syntaxes allowed for defining a constructor and a destructor of a class template. For example:
template <class> struct S { S(); ~S (); };
template <class T> S<T>::S<T>() { } // error
template <class T> S<T>::~S<T>() { } // okay
The reason for this is that 3.4.3.1 [class.qual] paragraph 2 says that S::S is “considered to name the constructor,” which is not a template and thus cannot accept a template argument list. On the other hand, the second S in S::~S finds the injected-class-name, which “can be used with or without a template-argument-list” (14.7.1 [temp.local] paragraph 1) and thus satisfies the requirement to name the destructor's class (12.4 [class.dtor] paragraph 1).
Would it make sense to allow the template-argument-list in the constructor declaration and thus make the language just a little easier to use?
Rationale (July, 2007):
The CWG noted that the suggested change would be confusing in the case where the class template had both template and non-template constructors.
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.3 [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.3 [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.3 [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.8.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.2 [lex.phases] paragraph 8).
The subject line pretty much says it all. It's a possibility that hadn't ever occurred to me. I don't see any prohibition in the standard, and I also don't think the possibility introduces any logical inconsistencies. The proper behavior, presumably, would be to go through the list of already-constructed objects (not including the current one, since its constructor wouldn't have finished executing) and destroy them in reverse order. Not fundamentally hard, and I'm sure lots of existing implementations already do that.
I'm just not sure whether the standard was intended to support this, or whether it's just that nobody else thought of it either. If the former, then a non-normative note somewhere in 3.6.2 [basic.start.init] might be nice.
Rationale (October 2004):
There is nothing in the Standard to indicate that this usage is prohibited, so it must be presumed to be permitted.
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.
Following the definition in 9 [class] paragraph 4 the following is a valid POD (actually a POD-struct):
struct test
{
const int i;
};
The legality of PODs with const members is also implied by the text of 5.3.4 [expr.new] paragraph 15 bullet 1, sub-bullet 2 and 12.6.2 [class.base.init] paragraph 4 bullet 2.
3.9 [basic.types] paragraph 3 states that
For any POD type T, if two pointers to T point to distinct objects obj1 and obj2, if the value of obj1 is copied into obj2, using the memcpy library function, obj2 shall subsequently hold the same value as obj1.
[Note: this text was changed by TC1, but the essential point stays the same.]
This implies that the following is required to work:
test obj1 = { 1 };
test obj2 = { 2 };
memcpy( &obj2, &obj1, sizeof(test) );
The memcpy of course changes the value of the const member, surely something that shouldn't be allowed.
Suggested resolution:
It is recommended that 3.9 [basic.types] paragraph 3 be reworded to exclude PODs which contain (directly or indirectly) members of const-qualified type.
Rationale (October, 2004):
7.1.6.1 [dcl.type.cv] paragraph 4 already forbids modifying a const member of a POD struct. The prohibition need not be repeated in 3.9 [basic.types].
3.9 [basic.types] paragraph 11 requires that a class type have a trivial copy constructor in order to be classified as a literal type. This seems overly restrictive; presumably having a constexpr copy constructor would suffice. (Note that a trivial copy constructor is a constexpr constructor according to 7.1.5 [dcl.constexpr] paragraph 4.)
Rationale (June, 2008):
A copy constructor takes a reference as its first parameter, thus no user-declared copy constructor can be constexpr.
3.9.1 [basic.fundamental] paragraph 6 states,
As described below, bool values behave as integral types.
This sentence looks definitely out of order: how can a value behave as a type?
Suggested resolution:
Remove the sentence entirely, as it doesn't supply anything that isn't already stated in the following paragraphs and in the referenced section about integral promotion.
Rationale (July, 2007):
This is, at most, an editorial issue with no substantive impact. The suggestion has been forwarded to the project editor for consideration.
The C++ standard says in 3.10 [basic.lval], in paragraph 15:
an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union),
Note that it is a literal copy from the C standard, but this is of course not the problem.
In C, union is not defined as an aggregate type. Therefore it is appropriate to say “aggregate or union.” But things changed in C++: aggregate type includes union type now (though not all unions are aggregates), and it becomes clear that the “union” in “aggregate or union” is redundant and should be deleted.
The above cited paragraph could be changed to:
an aggregate type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate)
Rationale (October, 2006):
As noted in the issue, not all unions are aggregates, but those that are not aggregates still allow aliasing. That part of the specification would be lost with the suggested change.
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.
Section 4.9 [conv.fpint] paragraph 1 states:
An rvalue of a floating point type can be converted to an rvalue of an integer type. The conversion truncates; that is, the fractional part is discarded.
Here, the concepts of “truncation” and “fractional part” seem to be used without precise definitions. When -3.14 is converted into an integer, is the truncation toward zero or away from zero? Is the fractional part -0.14 or 0.86? The standard seem to give no clear answer to these.
Suggested resolution:
Replace “truncates” with “truncates toward zero.”
Replace “the fractional part” with “the fractional part (where that of x is defined as x-floor(x) for nonnegative x and x-ceiling(x) for negative x);” there should be a better wording for this, or the entire statement “that is, the fractional part is discarded” can be removed, once the meaning of “truncation” becomes unambiguous as above.
Rationale (October, 2006):
The specification is clear enough: “fractional part” refers to the digits following the decimal point, so that -3.14 converted to int becomes -3.
In the following code, I expect both "null" and "FALSE" to be null pointer constants -- and that the code should compile and output the string "int*" twice to cout:
#include <iostream>
using namespace std;
void foo(int* p)
{
cout << "int*" << endl;
}
int main(void)
{
const int null = 0;
foo(null);
const bool FALSE = false;
foo(FALSE);
}
ISO/IEC 14882-1998 4.10 [conv.ptr] states:
An integral constant expression rvalue of integer type that evaluates to zero (called a /null pointer constant/) can be converted to a pointer type.
Stroustrup appears to agree with me -- he states (3rd edition page 88):
In C, it has been popular to define a macro NULL to represent the zero pointer. Because of C++`s tighter type checking, the use of plain 0, rather than any suggested NULL macro, leads to fewer problems. If you feel you must define NULL, use:const int NULL = 0;
However gcc 3.3.1 rejects this code with the errors:
bug.cc:17: error: invalid conversion from `int' to `int*'
bug.cc:19: error: cannot convert `const bool' to `int*' for argument `1' to `
void foo(int*)'
I have reported this as a bug (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=13867), but the gcc team states that 4.10 requires that a null pointer constant must be an rvalue -- and no implicit conversion from an lvalue to an rvalue is required (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=396):
a null pointer constant is an integral constant expression rvalue that evaluates to zero [4.10/1] in this case `null' is an lvalue. The standard does not specify that lvalue->rvalue decay happens here, so `null' is not a null pointer constant.
I disagree with the gcc teams interpretation -- I don't see why 3.10 [basic.lval] doesn't apply:
Whenever an lvalue appears in a context where an rvalue is expected, the lvalue is converted to an rvalue;
The insertion of the word rvalue appears to have occurred during standardization -- it is not present in either Stroustrup 2nd edition or the 3rd edition. Does the committee deliberately intend to exclude an lvalue as a null pointer constant by adding the word rvalue? If so, it leads to the rather bizarre fact that "null" is not a null pointer constant, but "null + 0" is!
Notes from the March 2004 meeting:
We think this is just a bug in gcc. The const variable does get converted to an rvalue in this context. This case is not really any different than cases like
const int null = 0; int i = null;or
const int i = 1; int a[i];(which are accepted by gcc). No one would argue that the second lines of those examples are invalid because the variables are lvalues, and yet the conversions to rvalue happen implicitly for the same reason cited above -- the contexts require an rvalue.
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.
[Picked up by evolution group at October 2002 meeting.]
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 : 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.
Note (March, 2008):
The Evolution Working Group recommended closing this issue with no further consideration. See paper J16/07-0033 = WG21 N2173.
In 5.3.1 [expr.unary.op], part of paragraph 7 describes how to compute the negative of an unsigned quantity:
The negative of an unsigned quantity is computed by subtracting its value from 2n, where n is the number of bits in the promoted operand. The type of the result is the type of the promoted operand.
According to this method, -0U will get the value 2n - 0 = 2n, where n is the number of bits in an unsigned int. However, 2n is obviously out of the range of values representable by an unsigned int and thus not the actual value of -0U. To get the result, a truncating conversion must be applied.
Rationale (April, 2007):
As noted in the issue description, a “truncating conversion” is needed. This conversion is supplied without need of an explicit mention, however, by the nature of unsigned arithmetic given in 3.9.1 [basic.fundamental] paragraph 4:
Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer.
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.
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.
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] .)
Code that was portable in C90 and C++98 is no longer portable with the introduction of data types longer than long; code that could previously cast size_t and ptrdiff_t to long without loss of precision (because long was the largest type) can no longer rely on that idiom.
The CWG discussed this during the Berlin (April, 2006) meeting. The general consensus was that this was unavoidable: there are valid reasons for implementations to keep long at a size less than that required for address arithmetic.
See paper J16/06-0053 = WG21 N1983, which also suggests the possibility of required diagnostics for problematic cases as an alternative to restricting the size of size_t and ptrdiff_t.
Rationale (October, 2006):
This is not an area in which the Standard should override the decisions of implementors who wish to maintain the size of long for backward compatibility but need a larger size_t to deal with expanded address spaces. Also, diagnostics of the sort described are better treated as quality of implementation issues rather than topics for standardization.
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.14.2 [lex.icon] : No mention of true or false as an integer literal.
From 2.14.6 [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.
According to 5.19 [expr.const] paragraph 1,
In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.
Given a case like
enum E { e };
int operator+(int, E);
int i[4 + e];
does this mean that the overloaded operator+ is not considered (because it can't be called), or is it selected by overload resolution, thus rendering the program ill-formed?
Rationale (April, 2005):
All expressions, including constant expressions, are subject to overload resolution. The example is ill-formed.
When jumping past initialization of a local static variable the value of the static becomes indeterminate. Seems like this behavior should be illegal just as it is for local variables with automatic linkage.
Here is an example:
struct X {
X(int i) : x(i) {}
int x;
};
int f(int c) {
if (c)
goto ly; // error here for jumping past next stmt.
static X a = 1;
ly:
return a.x; // either 1 or 0 depending on implementation.
}
6.7 [stmt.dcl] P3 should be changed to:
A program that jumps from a point where a local variable with automatic or static storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has POD type (3.9) and is declared without an initializer (8.5).This would imply "static X a = 1;" should be flagged as an error. Note that this behavior a may be a "quality of implementation issue" which may be covered in 6.7 P4. Paragraph 4 seems to make the choice of static/dynamic initialization indeterminate. Making this an error and thus determinate seems the correct thing to do since that is what is already required of automatic variables.
Steve Adamczyk: Some version of this may be appropriate, but it's common to have code that is executed only the first time it is reached, and to have an initialization of a static variable inside such a piece of code. In such a case, on executions after the first there is indeed a jump over the declaration, but the static variable is correctly initialized -- it was initialized the first time the routine was called.
void f() {
static bool first_time = true;
if (!first_time) goto after_init;
static int i = g();
first_time = false;
after_init:
...
}
Rationale (October, 2004):
The CWG sees no reason to change this specification. Local static variables are different from automatic variables: automatic variables, if not explicitly initialized, can have indeterminate (“garbage”) values, including trap representations, while local static variables are subject to zero initialization and thus cannot have garbage values.
The latitude granted to implementations regarding performing dynamic initialization of local static objects as if it were static initialization is exactly parallel to namespace scope objects (3.6.2 [basic.start.init]), as are the restrictions on programmer assumptions.
Because a definition is also a declaration, it might make sense to change uses of "declaration or definition" to simply "declaration".
Notes from the March 2004 meeting:
Jens Maurer prepared drafting for this issue, but we find ourselves reluctant to actually make the changes. Though correct, they seemed more likely to be misread than the existing wording.
Proposed resolution:
Remove in 1.3 [intro.defs] “parameter” the indicated words:
an object or reference declared as part of a function declarationor definition, or in the catch clause of an exception handler, that acquires a value on entry to the function or handler; ...
Remove in 14.2 [temp.param] paragraph 10 the indicated words:
The set of default template-arguments available for use with a template declarationor definitionis obtained by merging the default arguments from the definition (if in scope) and all declarations in scope in the same way default function arguments are (...).
Remove in 14.7 [temp.res] paragraph 2 the indicated words:
A name used in a template declarationor definitionand that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.
Remove in 14.7.4.1 [temp.point] paragraph 1 the indicated words:
Otherwise, the point of instantiation for such a specialization immediately follows the namespace scope declarationor definitionthat refers to the specialization.
Remove in 14.7.4.1 [temp.point] paragraph 3 the indicated words:
Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declarationor definitionthat refers to the specialization.
Remove in 14.8.3 [temp.expl.spec] paragraph 21 the indicated words:
Default function arguments shall not be specified in a declarationor a definitionfor one of the following explicit specializations:[Note: default function arguments may be specified in the declaration
- ...
or definitionof a member function of a class template specialization that is explicitly specialized. ]
Remove in 14.9.2.5 [temp.deduct.type] paragraph 18 the indicated words:
[Note: a default template-argument cannot be specified in a function template declarationor definition; ...]
Remove in 17.6.2.2 [using.headers] paragraph 3 the indicated words:
A translation unit shall include a header only outside of any external declarationor definition, and shall include the header lexically before the first reference to any of the entities it declaresor first definesin that translation unit.
Rationale (October, 2004):
CWG felt that readers might misunderstand “declaration” as meaning “non-definition declaration.”
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.
In clause 7.1.2 [dcl.fct.spec], para. 3, the following sentence
A function defined within a class definition is an inline function.
should, if I am not mistaken, instead be:
A function defined within a class declaration is an inline function."
Notes from October 2002 meeting:
This is not a defect. Though there is a long history, going back to the ARM, of use of the term "class declaration" to mean the definition of the class, we believe "class definition" is clearer. We have opened issue 379 to deal with changing all other uses of "class declaration" to "class definition" where appropriate.
A customer reports that when he attempts to replace ::operator new with a user-defined function, the standard library calls the default function by preference if the user-defined function is inline. I believe that our compiler is correct, and that such a replacement function isn't allowed to be inline, but I'm not sure there's sufficiently explicit language in the standard.
In general, of course, the definition of an inline function must be present in every translation unit where the function is called. (7.1.2 [dcl.fct.spec], par 4) It could be argued that this requirement doesn't quite address replacement functions: what we're dealing with is the odd case where we've already got one definition and the user is supplying a different one. I'd like to see something specifically addressing the case of a replacement function.
So what do we have? I see discussion of requirement for a replacement ::operator new in three places: 17.6.3.6 [replacement.functions], 18.6.1.1 [new.delete.single] par 2, and 3.7.4 [basic.stc.dynamic] par 2-3. I don't see anything explicitly saying that the replacement function may not be inline. The closest I can find is 18.6.1.1 [new.delete.single] par 2, which says that "a C++ program may define a function with this function signature that displaces the default version defined by the C++ Standard library". One might argue that "with this function signature" rules out inline, but that strikes me as a slight stretch.
Have I missed anything?
Andrew Koenig: I think you've turned up a problem in 7.1.2 [dcl.fct.spec] paragraph 4. Consider:
// Translation unit 1
#include <iostream>
extern void foo(void (*)());
inline void bar() {
std::cout << "Hello, world!" << std::endl;
}
int main() {
foo(bar);
}
// Translation unit 2
void foo(void (*f)()) { (*f)(); }
Are you really trying to tell me that this program is ill-formed because the definition of bar is not available in translation unit 2?
I think not. The actual words in 7.1.2 [dcl.fct.spec] par 4 are
An inline function shall be defined in every translation unit in which it is used...and I think at in this context, ``used'' should be interpreted to mean that foo is used only in translation unit 1, where it is converted to a value of type void(*)().
Notes from October 2003 meeting:
We don't think Andy Koenig's comment requires any action; "used" is already defined appropriately.
We agree that this replacement should not be allowed, but we think it's a library issue (in the rules for allowed replacements). Forwarded to library group; it's issue 404 on the library issues list.
Is the following valid?
template <class T> void f(T) {
typedef int x;
typedef T x;
}
int main() {
f(1);
}
There is an instantiation where the function is valid. Is an implementation allowed to issue an error on the template declaration because the types on the typedef are not the same (7.1.3 [dcl.typedef])?
How about
typedef T x; typedef T2 x;?
It can be argued that these cases should be allowed because they aren't necessarily wrong, but it can also be argued that there's no reason to write things like the first case above, and if such a case appears it's more likely to be a mistake than some kind of intentional test that int and T are the same type.
Notes from the October 2003 meeting:
We believe that all these cases should be allowed, and that errors should be required only when an instance of the template is generated. The current standard wording does not seem to disallow such cases, so no change is required.
9.2 [class.mem] paragraph 2 says,
A class is considered a completely-defined object type (3.9 [basic.types]) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments, and exception-specifications (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.
In particular, the return type of a member function is not listed as a context in which the class type is considered complete; instead, that case is handled as an exception to the general rule in 8.3.5 [dcl.fct] paragraph 6 requiring a complete type in the definition of a function:
The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).
These rules have implications for the use of decltype. (The following examples use the not-yet-accepted syntax for specifying the return type of a function after its declarator, but the questions apply to the current syntax as well.) Consider:
struct deduced {
int test() { return 0; }
auto eval( deduced& d )->decltype( d.test() ) {
return d.test();
}
};
5.2.5 [expr.ref] paragraph 1 requires that the class type of the object or pointer expression in a class member access expression be complete, so this usage is ill-formed.
A related issue is the use of this in a decltype specifier:
struct last_one {
int test() { return 0; }
auto eval()->decltype( this->test() ) {
return test();
}
};
9.3.2 [class.this] paragraph 1 allows use of this only in the body of a non-static member function, and the return type is not part of the function-body.
Do we want to change the rules to allow these kinds of decltype expressions?
Rationale (February, 2008):
In the other cases where a class type is considered complete within the definition of the class, it is possible to defer handling the construct until the end of the definition. That is not possible for types, as the type may be needed immediately in subsequent declarations.
It was also noted that the primary utility of decltype is in generic contexts; within a single class definition, other mechanisms are possible (e.g., use of a member typedef in both the declaration of the operand of the decltype and to replace the decltype itself).
The first bullet of 7.1.6.2 [dcl.type.simple] paragraph 4 says,
There are two clarifications to this specification that would assist the reader. First, it would be useful to have a note highlighting the point that a parenthesized expression is neither an id-expression nor a member access expression.
Second, the phrase “the type of the entity named by e” is unclear as to whether cv-qualification in the object or pointer expression is or is not part of that type. Rephrasing this to read, “the declared type of the entity,” or adding “(ignoring any cv-qualification in the object expression or pointer expression),” would clarify the intent.
Rationale (February, 2008):
The text is clear enough. In particular, both of these points are illustrated in the last two lines of the example contrasting decltype(a->x) and decltype((a->x)): in the former, the expression has no parentheses, thus satisfying the requirements of the first bullet and yielding the declared type of A::x, while the second has parentheses, falling into the third bullet and picking up the const from the object expression in the member access.
Because type deduction for the auto specifier is described in 7.1.6.4 [dcl.spec.auto] paragraph 6 as equivalent to the deduction that occurs in a call to a function template, the adjustment of the argument type from A to A& specified in 14.9.2.1 [temp.deduct.call] paragraph 3 is performed when the initializer is an lvalue. As a result, in the following example, ra has the type A& and not, as might be expected, A&&:
class A { };
void f() {
A a;
auto&& ra = a;
}
It is unclear whether this is surprising enough, and potentially widely-enough used, to warrant making an exception to the current rules to handle this case differently.
Rationale (September, 2008):
It is important that the deduction rules be the same in the function and auto cases. The result of this example might be surprising, but maintaining a consistent model for deduction is more important.
I received an inquiry/complaint that you cannot re-open a namespace using a qualified name. For example, the following program is ok, but if you uncomment the commented lines you get an error:
namespace A {
namespace N {
int a;
}
int b;
namespace M {
int c;
}
}
//namespace A::N {
// int d;
//}
namespace A {
namespace M {
int e;
}
}
int main()
{
A::N::a = 1;
A::b = 2;
A::M::c = 3;
// A::N::d = 4;
A::M::e = 5;
}
Andrew Koenig: There's a name lookup issue lurking here. For example:
int x;
namespace A {
int x;
namespace N {
int y;
};
}
namespace A::N {
int* y = &x; // which x?
}
Jonathan Caves: I would assume that any rule would state that:
namespace A::B {
would be equivalent to:
namespace A {
namespace B {
so in your example 'x' would resolve to A::x
BTW: we have received lots of bug reports about this "oversight".
Lawrence Crowl: Even worse is
int x;
namespace A {
int x;
}
namespace B {
int x;
namespace ::A {
int* y = &x;
}
}
I really don't think that the benefits of qualified names here is worth
the cost.
Notes from April 2003 meeting:
We're closing this because it's on the Evolution working group list.
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.6.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.6.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.)
Consider the following example:
class C {
public: enum E {};
friend void* operator new(size_t, E);
friend void operator delete(void*, E);
};
void foo() {
C::E e;
C* ptr = new(e) C();
}
This code, which is valid in global scope, becomes ill-formed when the class definition is moved into a namespace, and there is no way to make it valid:
namespace N {
class C {
public: enum E {};
friend void* operator new(size_t, E);
friend void operator delete(void*, E);
};
}
void foo() {
N::C::E e;
N::C* ptr = new(e) N::C();
}
The reason for this is that non-member allocation and deallocation functions are required to be members of the global scope (3.7.4.1 [basic.stc.dynamic.allocation] paragraph 1, 3.7.4.2 [basic.stc.dynamic.deallocation] paragraph 1), unqualified friend declarations declare names in the innermost enclosing namespace (7.3.1.2 [namespace.memdef] paragraph 3), and these functions cannot be declared in global scope at a point where the friend declarations could refer to them using qualified-ids because their second parameter is a member of the class and thus can't be named before the class containing the friend declarations is defined.
Possible solutions for this conundrum include invention of some mechanism to allow a friend declaration to designate a namespace scope other than the innermost enclosing namespace in which the friend class or function is to be declared or to relax the innermost enclosing namespace lookup restriction in 7.3.1.2 [namespace.memdef] paragraph 3 for friend declarations that nominate allocation and deallocation functions.
Rationale (April, 2006):
The CWG acknowledged that it is not always possible to move code from the global scope into a namespace but felt that this problem was not severe enough to warrant changing the language to accommodate it. Possible solutions include moving the enumeration outside the class or defining member allocation and deallocation functions.
Daveed Vandevoorde : 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 :
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.
Notes from the April 2003 meeting:
We decided to make no change and to close this issue as not-a-defect. This is not needed functionality; the example above, for example, can be written with ->template. This issue has been on the issues list for years as an extension, and there has been no clamor for it.
It was also noted that knowing that something is a template is not enough; there's still the issue of knowing whether it is a class or function template.
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.
Now that the concept of "conditionally-supported" is available (see N1564), perhaps asm should not be required of every implementation.
Rationale (October, 2004):
This is covered in paper N1627. We would like to keep asm as a keyword for all implementations, however, to enhance portability by preventing programmers from inadvertently using it as an identifier.
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-formed
For 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.
[Picked up by evolution group at October 2002 meeting.]
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.
Note (March, 2008):
The Evolution Working Group recommended closing this issue with no further consideration. See paper J16/07-0033 = WG21 N2173.
[Picked up by evolution group at October 2002 meeting.]
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.
Note (March, 2008):
The Evolution Working Group recommended closing this issue with no further consideration. See paper J16/07-0033 = WG21 N2173.
Is this code valid:
extern "C" void f();
namespace N
{
int var;
extern "C" void f(){ var = 10; }
}
The two declarations of f refer to the same external function, but is this a valid way to declare and define f?
And is the definition of f considered to be in namespace N or in the global namespace?
Notes from October 2002 meeting:
Yes, this example is valid. See 7.5 [dcl.link] paragraph 6, which contains a similar example with the definition in the global namespace instead. There is only one f, so the question of whether the definition is in the global namespace or the namespace N is not meaningful. The same function is found by name lookup whether it is found from the declaration in namespace N or the declaration in the global namespace, or both (7.3.4 [namespace.udir] paragraph 4).
Do we really need the & ref-qualifier? We could get the same behavior without it if we relaxed the restriction on ref-qualified and non-ref-qualified overloads in the same set:
| with the & ref-qualifier | without the & ref-qualifier |
struct S {
void f();
};
|
struct S {
void f();
};
|
struct S {
void f() &;
};
|
struct S {
void f();
void f() && = delete;
};
|
struct S {
void f() &&;
};
|
struct S {
void f() &&;
};
|
struct S {
void f() &;
void f() &&;
};
|
struct S {
void f();
void f() &&;
};
|
The main objection I can see to this change is that we would lose the notational convenience of the & ref-qualifier, which would need to be replaced by a pair of declarations. We might overcome this by still allowing a single & on a function (although it would not be a ref-qualifier) as a synonym to a non-ref-qualified declaration plus a deleted ref-qualified declaration.
The biggest asymmetry between the implicit object parameter and regular parameters is not in reference binding but in type deduction. Consider:
template <class R, class C, class A> void f(R (C::*p)(A));
With these members:
struct S {
void mv(std::string);
void mr(std::string&);
void ml(std::string&&);
};
then
f(&S::mv); // deduces A = string
f(&S::mr); // deduces A = string&
f(&S::ml); // deduces A = string&&
On the other hand, with these members:
struct S {
void mv(std::string);
void mr(std::string) &;
void ml(std::string) &&
};
then
f(&S::mv); // deduces C = S f(&S::mr); // illegal f(&S::ml); // illegal
To make template f work with any pointer to member function, I need three overloads of f. Add cv-qualifiers and it's twelve overloads!
And then there is the interaction with concepts. Consider this type:
struct Value {
Value& operator=(const Value&) &;
};
Is it, say, Regular? If so, will the following compile, and what is the outcome?
template <Regular T> void f() {
T() = T();
}
void g() {
f<Value>();
}
If Value is not Regular, that is a good motivation to avoid ever using & ref-qualifiers on operator= (and probably on any member functions).
If Value is Regular, then either f<Value>() doesn't compile, violating one of the principal motivations for concepts, or it calls Value::operator= on an rvalue, which was explicitly prohibited.
Rationale, March, 2009:
The CWG did not feel that the suggested change was a signficant improvement over the existing specification.
In deciding whether a construct is an object declaration or a function declaration, 8.2 [dcl.ambig.res] contains the following gem: "In that context, the choice is between a function declaration [...] and an object declaration [...] Just as for the ambiguities mentioned in 6.8 [stmt.ambig], the resolution is to consider any construct that could possibly be a declaration a declaration."
To what declaration do the last two "declarations" refer? Object, function, or (following from the syntax) possibly parameter declarations?
Notes from the 4/02 meeting:
This is not a defect. Section 8.2 [dcl.ambig.res] reads:
The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 [stmt.ambig] can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8 [stmt.ambig], the resolution is to consider any construct that could possibly be a declaration a declaration.
The wording "any construct" in the last sentence is not limited to top-level constructs. In particular, the function declaration encloses a parameter declaration, whereas the object declaration encloses an expression. Therefore, in case of ambiguity between these two cases, the declaration is parsed as a function declaration.
Consider the following program:
struct Point
{
Point(int){}
};
struct Lattice
{
Lattice(Point, Point, int){}
};
int main(void)
{
int a, b;
Lattice latt(Point(a), Point(b), 3); /* Line X */
}
The problem concerns the line marked /* Line X */, which is an ambiguous declarations for either an object or a function. The clause that governs this ambiguity is 8.2 [dcl.ambig.res] paragraph 1, and reads:
The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 [stmt.ambig] can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8 [stmt.ambig], the resolution is to consider any construct that could possibly be a declaration a declaration. [Note: a declaration can be explicitly disambiguated by a nonfunction-style cast, by a = to indicate initialization or by removing the redundant parentheses around the parameter name. ]
Based on this clause there are two possible interpretations of the declaration in line X:
Note that the last sentence before the "[Note:" is not much help, because both options are declarations.
Steve Adamczyk: a number of people replied to this posting on comp.std.c++ saying that they did not see a problem. The original poster replied:
I can't do anything but agree with your argumentation. So there is only one correct interpretation of clause 8.2 [dcl.ambig.res] paragraph 1, but I have to say that with some rewording, the clause can be made a lot clearer, like stating explicitly that the entire declaration must be taken into account and that function declarations are preferred over object declarations.
I would like to suggest the following as replacement for the current clause 8.2 [dcl.ambig.res] paragraph 1:
The ambiguity arising from the similarity between a functionstyle cast and a declaration mentioned in 6.8 [stmt.ambig] can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. The resolution is to consider any construct that could possibly be a function declaration a function declaration. [Note: To disambiguate, the whole declaration might have to be examined to determine if it is an object or a function declaration.] [Note: a declaration can be explicitly disambiguated by a nonfunction-style cast, by a = to indicate initialization or by removing the redundant parentheses around the parameter name. ]
Notes from the 4/02 meeting:
The working group felt that the current wording is clear enough.
Consider the following example:
struct S {
virtual void v() = 0;
};
void f(S sa[10]); // permitted?
8.3.4 [dcl.array] paragraph 1 says that a declaration like that of sa is ill-formed:
T is called the array element type; this type shall not be a reference type, the (possibly cv-qualified) type void, a function type or an abstract class type.
On the other hand, 8.3.5 [dcl.fct] paragraph 3 says that the type of sa is adjusted to S*, which would be permitted:
The type of each parameter is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively.
It is not clear whether the parameter adjustment trumps the prohibition on declaring an array of an abstract class type or not. Implementations differ in this respect: EDG 2.4.2 and MSVC++ 7.1 reject the example, while g++ 3.3.3 and Sun Workshop 8 accept it.
Rationale (April, 2005):
The prohibition in 8.3.4 [dcl.array] is absolute and does not allow for exceptions. Even though such a type in a parameter declaration would decay to an allowed type, the prohibition applies to the type before the decay.
This interpretation is consistent with the resolution of issue 337, which causes template type deduction to fail if such types are deduced. It was also observed that pointer arithmetic on pointers to abstract classes is very likely to fail, and the fact that the programmer used array notation to declare the pointer type is a strong indication that he/she expected to use subscripting.
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.
In section 8.5.3 [dcl.init.ref], paragraph 5, there is following note:
Note: the usual lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are not needed, and therefore are suppressed, when such direct bindings to lvalues are done.
I believe that this note is misleading. There should be either:
The problem:
int main()
{
const int ci = 10;
int * pi = NULL;
const int * & rpci = pi;
rpci = &ci;
*pi = 12; // circumvent constness of "ci"
}
int main()
{
int * pi = NULL;
const int * const & rcpci = pi; // 1
int i = 0;
pi = &i; // 2
if (pi == rcpci)
std::cout << "bound to lvalue" << std::endl;
else
std::cout << "bound to temporary rvalue" << std::endl;
}
There has been discussion on this issue on comp.lang.c++.moderated month ago, see http://groups.google.pl/groups?threadm=9bed99bb.0308041153.1c79e882%40posting.google.com and there seems to be some confusion about it. I understand that note is not normative, but apparently even some compiler writers are misled (try above code snippets on few different compilers, and using different compilation options - notably GCC 3.2.3 with -Wall -pedantic), thus it should be cleared up.
My proposal is to change wording of discussed note to:
Note: result of every standard conversion is never an lvalue, and therefore all standard conversions (clause 4) are suppressed, when such direct bindings to lvalues are done.
Rationale (April, 2005):
As acknowledged in the description of the issue, the referenced text is only a note and has no normative impact. Furthermore, the examples cited do not involve the conversions mentioned in the note, and the normative text is already sufficiently clear that the types in the examples are not reference-compatible.
Another instance to consider is that of invoking a member function from a null pointer:
struct A { void f () { } };
int main ()
{
A* ap = 0;
ap->f ();
}
Which is explicitly noted as undefined in 9.3.1 [class.mfct.non-static], even though one could argue that since f() is empty, there is no lvalue->rvalue conversion.
If f is static, however, there seems to be no such rule, and the call is only undefined if the dereference implicit in the -> operator is undefined. IMO it should be.
Incidentally, another thing that ought to be cleaned up is the inconsistent use of "indirection" and "dereference". We should pick one. (This terminology issue has been broken out as issue 342.)
This is related to issue 232
Rationale (October 2003):
We agreed the example should be allowed. p->f() is rewritten as (*p).f() according to 5.2.5 [expr.ref]. *p is not an error when p is null unless the lvalue is converted to an rvalue (4.1 [conv.lval]), which it isn't here.
Is this legal? Should it be?
struct E {
union {
struct { int x; } s;
} v;
};
One compiler faults a type definition (i.e. of the anonymous struct) since it is in an anonymous union [9.5 [class.union] paragraph 2: "The member-specification of an anonymous union shall only define non-static data members."].
I would suggest that compiler B is correctly interpreting the standard but that this is a defect in the standard. There is no reason to disallow definition of anonymous structs.
Furthermore, is it really necessary to disallow definition of named types in anonymous unions in general, as long as the types do not need fully qualified names for external linkage? Why should this be illegal?
struct E {
union {
typedef int Int;
struct X { X *next; Int n; } list;
} v;
};
Notes from October 2002 meeting:
There was agreement that the standard says such declarations are invalid; therefore this must be considered as an extension. There was general feeling that this extension would not be too useful, though Jason Merrill was sympathetic to the argument. It was also agreed that if this were to be changed it would require careful wording so as not to allow too many cases.
Note (March, 2008):
The Evolution Working Group recommended closing this issue with no further consideration. See paper J16/07-0033 = WG21 N2173.
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.
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.
The Standard does not appear to specify how to handle cases in which conflicting access specifications for a member are inherited from different base classes. For example,
struct A {
public:
int i;
};
struct B : virtual public A {
protected:
using A::i;
};
struct C : virtual public A, public B {
// "i" is protected from B, public from A
};
This question affects both the existing wording of 11.2 [class.access.base] paragraph 4 (“m as a member of N is public ... m as a member of N is private ... m as a member of N is protected”) and the proposed wording for issue 385 (“when a nonstatic data member or nonstatic member function is a protected member of its naming class”).
One possible definition of “is public” would be something like, “if any visible declaration of the entity has public access.” One could also plausibly define the access of m in N to be the minimum of all the visible declarations, or even an error if the visible declarations are inconsistent.
11.2 [class.access.base] paragraph 1 describes the access of inherited members, so a clarifying statement resolving this issue might plausibly be inserted at the end of that paragraph.
Proposed resolution (October, 2004):
Add the following text as a new paragraph after 11.2 [class.access.base] paragraph 1:
If a given base class can be reached along more than one path through a derived class's sub-object lattice (10.1 [class.mi]), a member of that base class could have different accessibility in the derived class along different paths. In such cases, the most permissive access prevails. [Example:
struct B { static int i; }; class I: protected B { }; class D1: public B, public I { }; class D2: public I, private B { };i is accessible