Document number:  J16/08-0016 = WG21 N2506
Date:  2008-02-03
Project:  Programming Language C++
Reference:  ISO/IEC IS 14882:2003
Reply to:  William M. Miller
 Edison Design Group, Inc.
 wmm@edg.com


C++ Standard Core Language Closed Issues, Revision 53


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 J16/07-0331 = WG21 N2461.


Issues with "NAD" Status


449. Consistency in use of hyphen with names of "non" entities

Section: 1.3  [intro.defs]     Status: NAD     Submitter: Daveed Vandevoorde     Date: 14 Jan 2004

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.




7. Can a class with a private virtual base class be derived from?

Section: 11.2  [class.access.base]     Status: NAD     Submitter: Jason Merrill     Date: unknown
    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.




17. Footnote 99 should discuss the naming class when describing members that can be accessed from friends

Section: 11.2  [class.access.base]     Status: NAD     Submitter: unknown     Date: unknown

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.




471. Conflicting inherited access specifications

Section: 11.2  [class.access.base]     Status: NAD     Submitter: Mike Miller     Date: 14 Jun 2004

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 as a public member of D1 and as a protected member of D2. —end example]

Rationale (03/2005): This question is already covered, in almost identical words, in 11.7 [class.paths].




209. Must friend declaration names be accessible?

Section: 11.4  [class.friend]     Status: NAD     Submitter: Judy Ward     Date: 1 Mar 2000

11.4 [class.friend], paragraph 7, says

A name nominated by a friend declaration shall be accessible in the scope of the class containing the friend declaration.

Does that mean the following should be illegal?

    class A { void f(); };
    class B { friend void A::f(); }; // Error: A::f not accessible from B

I discussed this with Bjarne in email, and he thinks it was an editorial error and this was not the committee's intention. The paragraph seems to have been added in the pre-Kona (24 Sept 1996) mailing, and I could not find anything in the previous meeting's (Stockholm) mailings which led me to believe this was intentional. The only compiler vendor which I think currently implements it is the latest release (2.43) of the EDG front end.

Proposed resolution (10/00):

Remove the first sentence of 11.4 [class.friend], paragraph 7.

Rationale (04/01):

After the 10/00 vote to accept this issue as a DR with the proposed resolution, it was noted that the first two sentences of 11 [class.access] paragraph 3 cause the proposed change to have no effect:

Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions. [Note: access control applies to names nominated by friend declarations (11.4 [class.friend]) and using-declarations (7.3.3 [namespace.udecl]). ]

In addition to the obvious editing to the text of the note, an exception to the blanket statement in the first sentence would also be required. However, discussion during the 04/01 meeting failed to produce consensus on exactly which names in the friend declaration should be exempted from the requirements of access control.

One possibility would be that only the name nominated as friend should be exempt. However, that approach would make it impossible to name a function as a friend if it used private types in its parameters or return type. Another suggestion was to ignore access for every name used in a friend declaration. That approach raised a question about access within the body of a friend function defined inline in the class body — the body is part of the declaration of a function, but references within the body of a friend function should still be subject to the usual access controls.

Other possibilities were raised, such as allowing the declaration of a friend member function if the declaration were permissible in its containing class, or taking the union of the access within the befriending class and the befriended entity. However, these suggestions would have been complex and difficult to specify correctly.

Ultimately it was decided that the original perceived defect was not sufficiently serious as to warrant the degree of complexity required to resolve it satisfactorily and the issue was consequently declared not to be a defect. It was observed that most of the problems involved with the current state of affairs result from inability to declare a particular member function as a friend; in such cases, an easy workaround is simply to befriend the entire class rather than the specific member function.




445. Wording issue on friend declarations

Section: 11.4  [class.friend]     Status: NAD     Submitter: Risto Lankinen     Date: 5 Dec 2003

Thus says the section 11.4 [class.friend]/7 in ISO 14882 C++ standard:

A name nominated by a friend declaration shall be accessible in the scope of the class containing the friend declaration.

The obvious intention of this is to allow a friend declaration to specify a function (or nested class, enum, etc.) that is declared "private" or "protected" in its enclosing class. However, literal interpretation seems to allow a broader access to the befriended function by the whole class that is declaring the friendship.

If the rule were interpreted literally as it is currently written, this would compile (when it, of course, shouldn't be allowed at all):

class C
{
private:
  static void f();
};

class D
{
  friend void C::f();  // A name nominated by friend declaration...

  D()
  {
   C::f();  // ... shall be accessible in scope of class declaring friendship
  }
};

Suggested fix is to reword "in the scope of the class containing the friend declaration" to exclude all other references from the scope of the declaring class, except the friend-declaration itself.

Notes from the March 2004 meeting:

We considered this and concluded that the standard is clear enough.




501. Visibility of friend declarations within the befriending class

Section: 11.4  [class.friend]     Status: NAD     Submitter: Gabriel Dos Reis     Date: 25 Jan 2005

I just received a query from a user of why line #1 in the following snippet is ill-formed:

    void g(int (*)(int));

    template<class T>
    class A {
       friend int f(int) { return 0; }

       void h() {
           g(f);          // #1
       }
    };

I believe that the special invisibility rule about friends is too complicated and makes life too complicated, especially considering that friends in templates are not templates, nor can they be conveniently rewritten with a “first declare at the namespace scope” rule. I can understand the rules when they make programming easier or prevent some obvious “silly” mistakes; but that does not seem to be the case here.

John Spicer: See two papers that discuss this issue: N0878 by Bill Gibbons, which ultimately gave rise to our current rules, and N0913 by me as an alternative to N0878.

Rationale (April, 2005):

The Standard is clear and consistent; this rule is the result of an explicit decision by the Committee.




19. Clarify protected member access

Section: 11.5  [class.protected]     Status: NAD     Submitter: unknown     Date: unknown

11.5 [class.protected] paragraph 1 says:

When a friend or a member function of a derived class references a protected nonstatic member of a base class, an access check applies in addition to ...
Instead of saying "references a protected nonstatic member of a base class", shouldn't this be rewritten to use the concept of naming class as 11.2 [class.access.base] paragraph 4 does?

Rationale (04/99): This rule is orthogonal to the specification in 11.2 [class.access.base] paragraph 4.




117. Timing of destruction of temporaries

Section: 12.2  [class.temporary]     Status: NAD     Submitter: Mike Miller     Date: 14 May 1999

12.2 [class.temporary] paragraph 4 seems self-contradictory:

the temporary that holds the result of the expression shall persist until the object's initialization is complete... the temporary is destroyed after it has been copied, before or when the initialization completes.
How can it be destroyed "before the initialization completes" if it is required to "persist until the object's initialization is complete?"

Rationale (04/00):

It was suggested that "before the initialization completes" refers to the case in which some part of the initialization terminates by throwing an exception. In that light, the apparent contradiction does not apply.




363. Initialization of class from self

Section: 12.6.1  [class.expl.init]     Status: NAD     Submitter: Sergey P. Derevyago     Date: 11 July 2002

Is the following code well-formed?

 struct A { /* */ };

 int main()
 {
  A a=a;
 }

Note, that { int a=a; } is pretty legal.

And if so, what is the semantics of the self-initialization of UDT? For example

 #include <stdio.h>

 struct A {
        A()           { printf("A::A() %p\n",            this);     }
        A(const A& a) { printf("A::A(const A&) %p %p\n", this, &a); }
        ~A()          { printf("A::~A() %p\n",           this);     }
 };

 int main()
 {
  A a=a;
 }

can be compiled and prints:

A::A(const A&) 0253FDD8 0253FDD8
A::~A() 0253FDD8

(on some implementations).

Notes from October 2002 meeting:

3.8 [basic.life] paragraph 6 indicates that the references here are valid. It's permitted to take the address of a class object before it is fully initialized, and it's permitted to pass it as an argument to a reference parameter as long as the reference can bind directly. Except for the failure to cast the pointers to void * for the %p in the printfs, these examples are standard-conforming.




307. Initialization of a virtual base class subobject

Section: 12.7  [class.cdtor]     Status: NAD     Submitter: Andrei Iltchenko     Date: 31 Aug 2001

The second paragraph of section 12.7 [class.cdtor] contains the following text:

To explicitly or implicitly convert a pointer (an lvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior.

Now suppose we have the following piece of code:

    struct  a  {
       a() :  m_a_data(0)  {   }
       a(const a& rhsa) :  m_a_data(rhsa.m_a_data)  {   }
       int   m_a_data;
    };

    struct  b :  virtual a  {
       b() :  m_b_data(0)  {   }
       b(const b& rhsb) :  a(rhsb),  m_b_data(rhsb.m_b_data)  {   }
       int   m_b_data;
    };

    struct  c :  b  {
       c() :  m_c_data(0)  {   }
       c(const c& rhsc) :  a(rhsc),// Undefined behaviour when constru-
                                   // cting an object of type 'c'
                           b(rhsc),  m_c_data(rhsc.m_c_data)  {   }
       int   m_c_data;
    };

    int  main()
    {   c   ac1,   ac2(ac1);   }

The problem with the above snippet is that when the value 'ac2' is being created and its construction gets started, c's copy constructor has first to initialize the virtual base class subobject 'a'. Which requires that the lvalue expression 'rhsc' be converted to the type of the parameter of a's copy constructor, which is 'const a&'. According to the wording quoted above, this can be done without undefined behaviour if and only if b's construction has already started, which is not possible since 'a', being a virtual base class, has to be initialized first by a constructor of the most derived object (12.6.2 [class.base.init]).

The issue could in some cases be alleviated when 'c' has a user-defined copy constuctor. The constructor could default-initialize its 'a' subobject and then initialize a's members as needed taking advantage of the latitude given in paragraph 2 of 12.6.2 [class.base.init].

But if 'c' ends up having the implicitly-defined copy constuctor, there's no way to evade undefined behaviour.

    struct  c :  b  {
       c() :  m_c_data(0)  {   }
       int   m_c_data;
    };

    int  main()
    {   c   ac1,   ac2(ac1);   }

Paragraph 8 of 12.8 [class.copy] states

The implicitly-defined copy constructor for class X performs a memberwise copy of its subobjects. The order of copying is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2 [class.base.init]). Each subobject is copied in the manner appropriate to its type:

Which effectively means that the implicitly-defined copy constructor for 'c' will have to initialize its 'a' base class subobject first and that must be done with a's copy constructor, which will always require a conversion of an lvalue expression of type 'const c' to an lvalue of type 'const a&'. The situation would be the same if all the three classes shown had implicitly-defined copy constructors.

Suggested resolution:

Prepend to paragraph 2 of 12.7 [class.cdtor] the following:

Unless the conversion happens in a mem-initializer whose mem-initializer-id designates a virtual base class of X, to explicitly or implicitly convert ...

Notes from the 10/01 meeting:

There is no problem in this example. ac1 is fully initialized before it is used in the initialization of ac2.




26. Copy constructors and default arguments

Section: 12.8  [class.copy]     Status: NAD     Submitter: Daveed Vandevoorde     Date: 22 Sep 1997

The working paper is quite explicit about

    struct X {
         X(X, X const& = X());
    };
being illegal (because of the chicken & egg problem wrt copying.)

Shouldn't it be as explicit about the following?

    struct Y {
        Y(Y const&, Y = Y());
    };
Rationale: There is no need for additional wording. This example leads to a program which either fails to compile (due to resource limits on recursive inlining) or fails to run (due to unterminated recursion). In either case the implementation may generate an error when the program is compiled.


356. Wording of behavior of generated copy constructor for scalar members

Section: 12.8  [class.copy]     Status: NAD     Submitter: Steve Clamage     Date: 25 May 2002

Section 12.8 [class.copy] paragraph 8 says the compiler-generated copy constructor copies scalar elements via the built-in assignment operator. Seems inconsistent. Why not the built-in initialization?

Notes from October 2002 meeting:

The Core Working Group believes this should not be changed. The standard already mentions built-in operators and the assignment operator does clearly define what must be done for scalar types. There is currently no concept of built-in initialization.




444. Overriding and the generated copy assignment operator

Section: 12.8  [class.copy]     Status: NAD     Submitter: Kerch Holt     Date: 26 Nov 2003

EDG (and g++, for that matter) picks the explicit copy assignment operator, which we think is wrong in this case:

#include <stdio.h>
struct D;   // fwd declaration
struct B {
    D& operator=(D&);
};
struct D : B {
    D() {}
    D(int ii) { s = ii; }
    using B::operator=;
    int s;
};
int main() {
    D od, od1(10);
    od = od1; // implicit D::operator=(D&) called, not BASE::operator=(D&)
}
D& B::operator=(D& d) { 
    printf("B::operator called\n");
    return d;
}

If you look at 12.8 [class.copy] paragraph 10 it explicitly states that in such a case the "using B::operator=" will not be considered.

Steve Adamczyk: The fact that the operator= you declared is (D&) and not (const D&) is fooling you. As the standard says, the operator= introduced by the using-declaration does not suppress the generation of the implicit operator=. However, the generated operator= has the (const D&) signature, so it does not hide B::operator=; it overloads it.

Kerch Holt: I'm not sure this is correct. Going by 12.8 P10 first paragraph we think that the other form "operator=(D&)" is generated because the two conditions mentioned were not met in this case. 1) there is no direct base with a "const [volatile] B&" or "B" operator 2) And no member has a operator= either. This implies the implicit operator is "operator=(D&)". So, if that is the case the "hiding" should happen.

Also, in the last paragraph it seems to state that operators brought in from "using", no matter what the parameter is, are always hidden.

Steve Adamczyk: Not really. I think this section is pretty clear about the fact that the implicit copy assignment operator is generated. The question is whether it hides or overloads the one imported by the using-declaration.

Notes from the March 2004 meeting:

(a) Class B does get an implicitly-generated operator=(const B&); (b) the using-declaration brings in two operator= functions from B, the explicitly-declared one and the implicitly-generated one; (c) those two functions overload with the implicitly-generated operator=(const D&) in the derived class, rather than being hidden by the derived-class function, because it does not match either of their signatures; (d) overload resolution picks the explicitly-declared function from the base class because it's the best match in this case. We think the standard wording says this clearly enough.




102. Operator lookup rules do not work well with parts of the library

Section: 13.3.1.2  [over.match.oper]     Status: NAD     Submitter: Herb Sutter     Date: 15 Oct 1998

The following example does not work as one might expect:

    namespace N { class C {}; }
    int operator +(int i, N::C) { return i+1; }

    #include <numeric>
    int main() {
        N::C a[10];
        std::accumulate(a, a+10, 0);
    }
According to 3.4.1 [basic.lookup.unqual] paragraph 6, I would expect that the "+" call inside std::accumulate would find the global operator+. Is this true, or am I missing a rule? Clearly, the operator+ would be found by Koenig lookup if it were in namespace N.

Daveed Vandevoorde: But doesn't unqualified lookup of the operator+ in the definition of std::accumulate proceed in the namespace where the implicit specialization is generated; i.e., in namespace std?

In that case, you may find a non-empty overload set for operator+ in namespace std and the surrounding (global) namespace is no longer considered?

Nathan Myers: Indeed, <string> defines operator+, as do <complex>, <valarray>, and <iterator>. Any of these might hide the global operator.

Herb Sutter: These examples fail for the same reason:

    struct Value { int i; };

    typedef map<int, Value > CMap;
    typedef CMap::value_type CPair;

    ostream & operator<< ( ostream &os, const CPair &cp )
      { return os << cp.first << "/" << cp.second.i; }

    int main() {
      CMap courseMap;
      copy( courseMap.begin(), courseMap.end(),
            ostream_iterator<CPair>( cout, "\n" ) );
    }

    template<class T, class S>
    ostream& operator<< (ostream& out, pair<T,S> pr)
      { return out << pr.first << " : " << pr.second << endl; }

    int main() {
      map <int, string> pl;
      copy( pl.begin(), pl.end(),
            ostream_iterator <places_t::value_type>( cout, "\n" ) );
    }
This technique (copying from a map to another container or stream) should work. If it really cannot be made to work, that would seem broken to me. The reason is does not work is that copy and pair are in namespace std and the name lookup rules do not permit the global operator<< to be found because the other operator<<'s in namespace std hide the global operator. (Aside: FWIW, I think most programmers don't realize that a typedef like CPair is actually in namespace std, and not the global namespace.)

Bill Gibbons: It looks like part of this problem is that the library is referring to names which it requires the client to declare in the global namespace (the operator names) while also declaring those names in namespace std. This would be considered very poor design for plain function names; but the operator names are special.

There is a related case in the lookup of operator conversion functions. The declaration of a conversion function in a derived class does not hide any conversion functions in a base class unless they convert to the same type. Should the same thing be done for the lookup of operator function names, e.g. should an operator name in the global namespace be visible in namespace std unless there is a matching declaration in std?

Because the operator function names are fixed, it it much more likely that a declaration in an inner namespace will accidentally hide a declaration in an outer namespace, and the two declarations are much less likely to interfere with each other if they are both visible.

The lookup rules for operator names (when used implicitly) are already quite different from those for ordinary function names. It might be worthwhile to add one more special case.

Mike Ball : The original SGI proposal said that non-transitive points of instantiation were also considered. Why, when, and by whom was it added?

Rationale (10/99): This appears to be mainly a program design issue. Furthermore, any attempt to address it in the core language would be beyond the scope of what can be done in a Technical Corrigendum.




423. Can a conversion be done on the left operand of a compound assignment?

Section: 13.3.1.2  [over.match.oper]     Status: NAD     Submitter: Gennaro Prota     Date: 19 Jun 2003

Is the following well-formed?

  template <typename T>
  class test
  {
  public:
    operator T& (){ return m_i; }
  private:
    T m_i;
  }; 
  int main()
  {
    test<int*> t2;
    t2 += 1;  // Allowed?
  }

Is it possible that by "assignment operators" (13.3.1.2 [over.match.oper] paragraph 4) only the built-in candidates for operator= (i.e. excluding +=, *=, etc.) were meant? On one hand the plural ("operators") seems to imply that all the assignment operators are considered. OTOH, there has already been a core DR (221) about a missing distinction between "assignment operator" and "compound assignment operators". Is there a similar defect here?

Steve Adamczyk: The standard is ambiguous. However, I think the ARM was fairly clear about "assignment operators" meaning only "=", and I find that Cfront 3.0.1 accepts the test case (with typename changed to class). I don't know whether that's good or bad, but it's at least a precedent. Given the change of Core Issue 221, if we do nothing further conversions are valid on += and therefore this case is valid.

Note that "t2++;" is unquestionably valid, so one could also argue for the status quo (post-221) on the basis of consistency.

Notes from the October 2003 meeting:

We believe the example is well-formed, and no change other than that in issue 221 is needed.




243. Weighting of conversion functions in direct-initialization

Section: 13.3.3.1.2  [over.ics.user]     Status: NAD     Submitter: Steve Adamczyk     Date: 5 Sep 2000

There is a moderately serious problem with the definition of overload resolution. Consider this example:

    struct B;
    struct A {
        A(B);
    };
    struct B {
        operator A();
    } b;
    int main() {
        (void)A(b);
    }

This is pretty much the definition of "ambiguous," right? You want to convert a B to an A, and there are two equally good ways of doing that: a constructor of A that takes a B, and a conversion function of B that returns an A.

What we discover when we trace this through the standard, unfortunately, is that the constructor is favored over the conversion function. The definition of direct-initialization (the parenthesized form) of a class considers only constructors of that class. In this case, the constructors are the A(B) constructor and the (implicitly-generated) A(const A&) copy constructor. Here's how they are ranked on the argument match:

A(B) exact match (need a B, have a B)
A(const A&) user-defined conversion (B::operator A used to convert B to A)

In other words, the conversion function does get considered, but it's operating with, in effect, a handicap of one user defined conversion. To put that a different way, this problem is a problem of weighting, not a problem that certain conversion paths are not considered.

I believe the reason that the standard's approach doesn't yield the "intuitive" result is that programmers expect copy constructor elision to be done whenever reasonable, so the intuitive cost of using the conversion function in the example above is simply the cost of the conversion function, not the cost of the conversion function plus the cost of the copy constructor (which is what the standard counts).

Suggested resolution:

In a direct-initialization overload resolution case, if the candidate function being called is a copy constructor and its argument (after any implicit conversions) is a temporary that is the return value of a conversion function, and the temporary can be optimized away, the cost of the argument match for the copy constructor should be considered to be the cost of the argument match on the conversion function argument.

Notes from 10/01 meeting:

It turns out that there is existing practice both ways on this issue, so it's not clear that it is "broken". There is some reason to feel that something that looks like a "constructor call" should call a constructor if possible, rather than a conversion function. The CWG decided to leave it alone.




61. Address of static member function "&p->f"

Section: 13.4  [over.over]     Status: NAD     Submitter: Steve Adamczyk     Date: 13 Oct 1998

Can p->f, where f refers to a set of overloaded functions all of which are static member functions, be used as an expression in an address-of-overloaded-function context? A strict reading of this section suggests "no", because "p->f" is not the name of an overloaded function (it's an expression). I'm happy with that, but the core group should decide and should add an example to document the decision, whichever way it goes.

Rationale (10/99): The "strict reading" correctly reflects the intent of the Committee, for the reason given, and no clarification is required.




247. Pointer-to-member casts and function overload resolution

Section: 13.4  [over.over]     Status: NAD     Submitter: Martin Sebor     Date: 22 Sep 2000

It is unclear whether the following code is well-formed or not:

    class A { }; 

    struct B : public A
    { 
	void foo (); 
	void foo (int);
    }; 

    int main ()
    {
	void (A::*f)() = (void (A::*)())&B::foo;
    }

13.4 [over.over] paragraph 1 says,

The function selected is the one whose type matches the target type required in the context. The target can be

This would appear to make the program ill-formed, since the type in the cast is different from that of either interpretation of the address-of-member expression (its class is A, while the class of the address-of-member expression is B)

However, 13.4 [over.over] paragraph 3 says,

Nonstatic member functions match targets of type "pointer-to-member-function;" the function type of the pointer to member is used to select the member function from the set of overloaded member functions.

The class of which a function is a member is not part of the "function type" (8.3.5 [dcl.fct] paragraph 4). Paragraph 4 is thus either a misuse of the phrase "function type," a contradiction of paragraph 1, or an explanation of what "matching the target type" means in the context of a pointer-to-member target. By the latter interpretation, the example is well-formed and B::foo() is selected.

Bill Gibbons: I think this is an accident due to vague wording. I think the intent was

The function selected is the one which will make the effect of the cast be that of the identity conversion.

Mike Miller: The "identity conversion" reading seems to me to be overly restrictive. It would lead to the following:

    struct B {
        void f();
        void f(int);
    };
    struct D: B { };
    void (D::* p1)() = &D::f;                  // ill-formed
    void (D::* p2)() = (void (B::*)()) &D::f;  // okay

I would find the need for an explicit cast here surprising, since the downcast is a standard conversion and since the declaration of p1 certainly has enough information to disambiguate the overload set. (See also issue 203.)

Bill Gibbons: There is an interesting situation with using-declarations. If a base class member function is present in the overload set in a derived class due to a using-declaration, it is treated as if it were a derived class member function for purposes of overload resolution in a call (13.3.1 [over.match.funcs] paragraph 4):

For non-conversion functions introduced by a using-declaration into a derived class, the function is considered to be a member of the derived class for the purpose of defining the type of the implicit object parameter

There is no corresponding rule for casts. Such a rule would be practical, but if the base class member function were selected it would not have the same class as that specified in the cast. Since base-to-derived pointer to member conversions are implicit conversions, it would seem reasonable to perform this conversion implicitly in this case, so that the result of the cast has the right type. The usual ambiguity and access restrictions on the base-to-derived conversion would not apply since they do not apply to calling through the using-declaration either.

On the other hand, if there is no special case for this, we end up with the bizarre case:

    struct A {
        void foo();
    };
    struct B : A {
        using A::foo;
        void foo(int);
    }
    int main() {
        // Works because "B::foo" contains A::foo() in its overload set.
        (void (A::*)())&B::foo;
        // Does not work because "B::foo(int)" does not match the cast.
        (void (A::*)(int))&B::foo;
    }

I think the standard should be clarified by either:

Rationale (10/00): The cited example is well-formed. The function type, ignoring the class specification, is used to select the matching function from the overload set as specified in 13.4 [over.over] paragraph 3. If the target type is supplied by an explicit cast, as here, the conversion is then performed on the selected pointer-to-member value, with the usual restrictions on what can and cannot be done with the converted value (5.2.9 [expr.static.cast] paragraph 9, 5.2.10 [expr.reinterpret.cast] paragraph 9).




27. Overload ambiguities for builtin ?: prototypes

Section: 13.6  [over.built]     Status: NAD     Submitter: Jason Merrill     Date: 25 Sep 1997

I understand that the lvalue-to-rvalue conversion was removed in London. I generally agree with this, but it means that ?: needs to be fixed:

Given:

    bool test;
    Integer a, b;
    test ? a : b;
What builtin do we use? The candidates are
    operator ?:(bool, const Integer &, const Integer &)
    operator ?:(bool, Integer, Integer)
which are both perfect matches.

(Not a problem in FDIS, but misleading.)

Rationale: The description of the conditional operator in 5.16 [expr.cond] handles the lvalue case before the prototype is considered.




552. Use of typename in the type in a non-type parameter-declaration

Section: 14.2  [temp.names]     Status: NAD     Submitter: Andre Morozov     Date: 15 December 2005

The discussion in of the use of typename with a qualified-id in a template parameter-declaration in 14.2 [temp.names] paragraph 2 is confusing:

typename followed by an unqualified-id names a template type parameter. typename followed by a qualified-id denotes the type in a non-type parameter-declaration.

This rule would be clearer if the unqualified-id case were described in terms of resolving the ambiguity of declaring a template parameter name versus referring to a type-name from the enclosing scope, and if the qualified-id case referred to the use of the typename keyword with dependent types in 14.6 [temp.res]. An example would also be helpful.

Rationale (April, 2006):

The CWG felt that the wording was already clear enough.




114. Virtual overriding by template member function specializations

Section: 14.5.2  [temp.mem]     Status: NAD     Submitter: Bill Gibbons     Date: 7 May 1999

According to 14.5.2 [temp.mem] paragraph 4,

A specialization of a member function template does not override a virtual function from a base class.
Bill Gibbons: I think that's sufficiently surprising behavior that it should be ill-formed instead.

As I recall, the main reason why a member function template cannot be virtual is that you can't easily construct reasonable vtables for an infinite set of functions. That doesn't apply to overrides.

Another problem is that you don't know that a specialization overrides until the specialization exists:

    struct A {
        virtual void f(int);
    };
    struct B : A {
        template<class T> void f(T);  // does this override?
    };
But this could be handled by saying: The last case might only involve non-deducible contexts, e.g.
    template<int I> struct X;
    struct A {
        virtual void f(A<5>);
    };
    struct B : A {
        template<int I, int J> void f(A<I+J>);  // does not overrride
    };

    void g(B *b) {
        X<t> x;
        b->f<3,2>(x);  // specialization B::f(A<5>) makes program ill-formed
    }
So I think there are reasonable semantics. But is it useful?

If not, I think the creation of a specialization that would have been an override had it been declared in the class should be an error.

Daveed Vandevoorde: There is real code out there that is written with this rule in mind. Changing the standard on them would not be good form, IMO.

Mike Ball: Also, if you allow template functions to be specialized outside of the class you introduce yet another non-obvious ordering constraint.

Please don't make such a change after the fact.

John Spicer: This is the result of an explicit committee decision. The reason for this rule is that it is too easy to unwittingly override a function from a base class, which was probably not what was intended when the template was written. Overriding should be a conscious decision by the class writer, not something done accidentally by a template.

Rationale (10/99): The Standard correctly reflects the intent of the Committee.

Notes from October 2002 meeting:

This was reopened because of a discussion while reviewing possible extensions.

Notes from April 2003 meeting:

This was discussed again, and the consensus was that we did not want to make a change, and in particular we did not want to make it an error and risk breaking existing code.




47. Template friend issues

Section: 14.5.4  [temp.friend]     Status: NAD     Submitter: John H. Spicer     Date: 7 Nov 1997

Issue 1

Paragraph 1 says that a friend of a class template can be a template. Paragraph 2 says: A friend template may be declared within a non-template class. A friend function template may be defined within a non-template class.

I'm not sure what this wording implies about friend template definitions within template classes. The rules for class templates and normal classes should be the same: a function template can be declared or defined, but a class template can only be declared in a friend declaration.

Issue 2

Paragraph 4 says: When a function is defined in a friend function declaration in a class template, the function is defined when the class template is first instantiated. I take it that this was intended to mean that a function that is defined in a class template is not defined until the first instantiation. I think this should say that a function that is defined in a class template is defined each time the class is instantiated. This means that a function that is defined in a class template must depend on all of the template parameters of the class template, otherwise multiple definition errors could occur during instantiations. If we don't have a rule like this, compilers would have to compare the definitions of functions to see whether they are the same or not. For example:

    template <class T> struct A {
            friend int f() { return sizeof(T); }
    };
    
    A<int> ai;
    A<long> ac;
I hope we would all agree that this program is ill-formed, even if long and int have the same size.

From Bill Gibbons:

[1] That sounds right.

[2] Whenever possible, I try to treat instantiated class templates as if they were ordinary classes with funny names. If you write:

    struct A_int {
        friend int f() { return sizeof(int); }
    };
    struct A_long {
        friend int f() { return sizeof(long); }
    };
it is a redefinition (which is not allowed) and an ODR violation. And if you write:
    template <class T, class U> struct A {
                friend int f() { return sizeof(U); }
    };
    
    A<int,float> ai;
    A<long,float> ac;
the corresponding non-template code would be:
    struct A_int_float {
        friend int f() { return sizeof(float); }
    };
    struct A_long_float {
        friend int f() { return sizeof(float); }
    };
then the two definitions of "f" are identical so there is no ODR violation, but it is still a redefinition. I think this is just an editorial clarification.

Rationale (04/99): The first sub-issue reflects wording that was changed to address the concern before the IS was issued. A close and careful reading of the Standard already leads to the conclusion that the example in the second sub-issue is ill-formed, so no change is needed.




316. Injected-class-name of template used as template template parameter

Section: 14.6.1  [temp.local]     Status: NAD     Submitter: Jason Merrill     Date: 14 Oct 2001

A gcc hacker recently sent in a patch to make the compiler give an error on code like this:

  template <template <typename> class T> struct A { };

  template <typename U> struct B
  {
    A<B> *p;
  };
presumably because the DR from issue 176 says that we decide whether or not B is to be treated as a template depending on whether a template-argument-list is supplied. I think this is a drafting oversight, and that B should also be treated as a template when passed as a template template parameter. The discussion in the issue list only talks about making the name usable both as a class and as a template.

John Spicer: This case was explicitly discussed and it was agreed that to use the injected name as a template template parameter you need to use the non-injected name.

A (possibly unstated) rule that I've understood about template arguments is that the form of the argument (type/nontype/template) is based only on the argument and not on the kind of template parameter. An example is that "int()" is always "function taking no arguments returning int" and never a convoluted way of saying zero.

In a similar way, we now decide whether or not something is a template based only on the form of the argument.

I think this rule is important for two kinds of cases. The first case involves explicit arguments of function templates:

  template <template <typename> class T> void f(){} // #1
  template <class T> void f(){}  // #2

  template <typename U> struct B {
	void g() {
		f<B>();
	}
  };

  int main() {
	B<int> b;
	b.g();
  }

With the current rules, this uses B as a type argument to template #2.

The second case involves the use of a class template for which the template parameter list is unknown at the point where the argument list is scanned:

  template <class T> void f(){}

  template <typename U> struct B {
	void g() {
		f< U::template X<B> >();  // what is B?
	}
  };

  struct Z1 {
	template <class T> struct X {};
  };

  struct Z2 {
	template <template <class> class T> struct X {};
  };

  int main() {
	B<Z1> b1;
	b1.g();

	B<Z2> b2;
	b2.g();
  }

If B could be used as a template name we would be unable to decide how to treat B at the point that it was scanned in the template argument list.

So, I think it is not an oversight and that it should be left the way it is.

Notes from the 4/02 meeting:

It was agreed that this is Not a Defect.


544. Base class lookup in explicit specialization

Section: 14.6.2  [temp.dep]     Status: NAD     Submitter: John Spicer     Date: 01 November 2005

There is some question as to whether 14.6.2 [temp.dep] paragraph 3 applies to the definition of an explicitly-specialized member of a class template:

In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

Consider an example like the following:

    template <class T>
    struct A {
     void foo() {}
    };

    template <class T>
    struct B: A<T> {
     int bar();
    };

    int foo() { return 0; }

    template <>
    int B<int>::bar() { return foo(); }

    int main() {
     return B<int>().bar();
    }

Does foo in the definition of B<int>::bar() refer to ::foo() or to A<int>::foo()?

Rationale (April, 2006):

An explicitly-specialized member of a class template is not, in fact, a member of a class template but a member of a particular specialization of that template. The special treatment of lookup vis-a-vis dependent base classes in 14.6.2 [temp.dep] thus does not apply, and base class members are found by unqualified name lookup.




334. Is a comma-expression dependent if its first operand is?

Section: 14.6.2.2  [temp.dep.expr]     Status: NAD     Submitter: John Spicer     Date: 10 Jan 2002

Is the comma expression in the following dependent?

  template <class T> static void f(T)
  {
  }
  template <class T> void g(T)
  {
    f((T::x, 0));
  }
  struct A {
    static int x;
  };
  void h()
  {
    g(A());
  }

According to the standard, it is, because 14.6.2.2 [temp.dep.expr] says that an expression is dependent if any of its sub-expressions is dependent, but there is a question about whether the language should say something different. The type and value of the expression are not really dependent, and similar cases (like casting T::x to int) are not dependent.

Mark Mitchell: If the first operand is dependent, how do we know it does not have an overloaded comma operator?

Rationale (October, 2004):

CWG agreed that such comma expressions are and ought to be dependent, for the reason expressed in Mark Mitchell's comment.




34. Argument dependent lookup and points of instantiation

Section: 14.7.1  [temp.inst]     Status: NAD     Submitter: Daveed Vandevoorde     Date: 15 Jul 1998

Does Koenig lookup create a point of instantiation for class types? I.e., if I say:

    TT<int> *p;
    f(p);
The namespaces and classes associated with p are those associated with the type pointed to, i.e., TT<int>. However, to determine those I need to know TT<int> bases and its friends, which requires instantiation.

Or should this be special cased for templates?

Rationale: The standard already specifies that this creates a point of instantiation.




489. Must member function templates be instantiated during overload resolution?

Section: 14.7.1  [temp.inst]     Status: NAD     Submitter: Daveed Vandevoorde     Date: 24 Nov 2004

A related question to that raised in issue 488 is whether member function templates must be instantiated if the compiler can determine that they will not be needed by the function selected by overload resolution. That is explicitly specified for class templates in 14.7.1 [temp.inst] paragraph 5:

If the overload resolution process can determine the correct function to call without instantiating a class template definition, it is unspecified whether that instantiation actually takes place.

Should the same be true for member function templates? In the example from issue 488,

    struct S {
        template <typename T> S(const T&);
    };
    void f(const S&);
    void f(int);
    void g() {
        enum E { e };
        f(e);    // ill-formed?
    }

a compiler could conceivably determine that f(int) would be selected by overload resolution (because it involves only an integral promotion, while the alternative requires a user-defined conversion) without instantiating the declaration of the S constructor. Should the compiler have that freedom?

Rationale (April, 2005):

In order for this question to come up, there would need to be a “gap” between the the normal rules and the rules for template argument deduction failure. The resolution for issue 488 will close the only such gap of which the CWG is aware. The issue can be reopened if other such cases turn up.




46. Explicit instantiation of member templates

Section: 14.7.2  [temp.explicit]     Status: NAD     Submitter: John H. Spicer     Date: 28 Jan 1998

Is the second explicit instantiation below well-formed?

    template <class T> struct A {
        template <class T2> void f(T2){}
    };

    template void A<int>::f(char); // okay
    
    template template void A<int>::f(float); // ?
Since multiple "template<>" clauses are permitted in an explicit specialization, it might follow that multiple "template" keywords should also be permitted in an explicit instantiation. Are multiple "template" keywords not allowed in an explicit instantiation? The grammar permits it, but the grammar permits lots of stuff far weirder than that. My opinion is that, in the absence of explicit wording permitting that kind of usage (as is present for explicit specializations) that such usage is not permitted for explicit instantiations.

Rationale (04/99): The Standard does not describe the meaining of multiple template keywords in this context, so the example should be considered as resulting in undefined behavior according to 1.3.13 [defns.undefined] .




3. The template compilation model rules render some explicit specialization declarations not visible during instantiation

Section: 14.7.3  [temp.expl.spec]     Status: NAD     Submitter: Bill Gibbons     Date: unknown

[N1065 issue 1.19] An explicit specialization declaration may not be visible during instantiation under the template compilation model rules, even though its existence must be known to perform the instantiation correctly. For example:

translation unit #1

      template<class T> struct A { };
      export template<class T> void f(T) { A<T> a; }
translation unit #2
      template<class T> struct A { };
      template<> struct A<int> { }; // not visible during instantiation
      template<class T> void f(T);
      void g() { f(1); }
Rationale: This issue was addressed in the FDIS and should have been closed.


88. Specialization of member constant templates

Section: 14.7.3  [temp.expl.spec]     Status: NAD     Submitter: Jason Merrill     Date: 20 Jan 1999

Is this valid C++? The question is whether a member constant can be specialized. My inclination is to say no.

    template <class T> struct A {
        static const T i = 0;
    };

    template<> const int A<int>::i = 42;
    
    int main () {
        return A<int>::i;
    }
John Spicer: This is ill-formed because 9.4.2 [class.static.data] paragraph 4 prohibits an initializer on a definition of a static data member for which an initializer was provided in the class.

The program would be valid if the initializer were removed from the specialization.

Daveed Vandevoorde: Or at least, the specialized member should not be allowed in constant-expressions.

Bill Gibbons: Alternatively, the use of a member constant within the definition could be treated the same as the use of "sizeof(member class)". For example:

    template <class T> struct A {
        static const T i = 1;
        struct B { char b[100]; };
        char x[sizeof(B)];     // specialization can affect array size
        char y[i];             // specialization can affect array size 
    };

    template<> const int A<int>::i = 42;
    template<> struct A<int>::B { char z[200] };

    int main () {
        A<int> a;
        return sizeof(a.x)   // 200  (unspecialized value is 100)
             + sizeof(a.y);  // 42   (unspecialized value is 1)
    }
For the member template case, the array size "sizeof(B)" cannot be evaluated until the template is instantiated because B might be specialized. Similarly, the array size "i" cannot be evaluated until the template is instantiated.

Rationale (10/99): The Standard is already sufficiently clear on this question.




182. Access checking on explicit specializations

Section: 14.7.3  [temp.expl.spec]     Status: NAD     Submitter: John Spicer     Date: 8 Nov 1999

John Spicer: Certain access checks are suppressed on explicit instantiations. 14.7.2 [temp.explicit] paragraph 8 says:

The usual access checking rules do not apply to names used to specify explicit instantiations. [Note: In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be accessible. ]
I was surprised that similar wording does not exist (that I could find) for explicit specializations. I believe that the two cases should be handled equivalently in the example below (i.e., that the specialization should be permitted).
    template <class T> struct C {
    void f();
    void g();
    };

    template <class T> void C<T>::f(){}
    template <class T> void C<T>::g(){}

    class A {
    class B {};
    void f();
    };

    template void C<A::B>::f();    // okay
    template <> void C<A::B>::g(); // error - A::B inaccessible

    void A::f() {
    C<B> cb;
    cb.f();
    }

Mike Miller: According to the note in 14.3 [temp.arg] paragraph 3,

if the name of a template-argument is accessible at the point where it is used as a template-argument, there is no further access restriction in the resulting instantiation where the corresponding template-parameter name is used.

(Is this specified anywhere in the normative text? Should it be?)

In the absence of text to the contrary, this blanket permission apparently applies to explicitly-specialized templates as well as to implicitly-generated ones (is that right?). If so, I don't see any reason that an explicit instantiation should be treated differently from an explicit specialization, even though the latter involves new program text and the former is just a placement instruction to the implementation.

Proposed Resolution (4/02):

In 14.7.2 [temp.explicit] delete paragraph 8:

The usual access checking rules do not apply to names used to specify explicit instantiations. [Note: In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be accessible. ]

In 14.7 [temp.spec] add the paragraph deleted above as paragraph 7 with the changes highlighted below:

The usual access checking rules do not apply to names used to specify explicit instantiations or explicit specializations. [Note: In particular, tThe template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be accessible. ]

Rationale (October 2002):

We reconsidered this and decided that the difference between the two cases (explicit specialization and explicit instantiation) is appropriate. The access rules are sometimes bent when necessary to allow naming something, as in an explicit instantiation, but explicit specialization requires not only naming the entity but also providing a definition somewhere.




285. Identifying a function template being specialized

Section: 14.7.3  [temp.expl.spec]     Status: NAD     Submitter: Erwin Unruh     Date: 01 May 2001

The Standard does not describe how to handle an example like the following:

    template <class T> int f(T, int);
    template <class T> int f(int, T);

    template<> int f<int>(int, int) { /*...*/ }

It is impossible to determine which of the function templates is being specialized. This problem is related to the discussion of issue 229, in which one of the objections raised against partial specialization of function templates is that it is not possible to determine which template is being specialized.

Notes from 10/01 meeting:

It was decided that while this is true, it's not a problem. You can't call such functions anyway; the call would be ambiguous.




99. Partial ordering, references and cv-qualifiers

Section: 14.8.2.1  [temp.deduct.call]     Status: NAD     Submitter: Mike Miller     Date: 5 Mar 1999

Consider:

    template <class T> void f(T&);
    template <class T> void f(const T&);
    void m() {
        const int p = 0;
        f(p);
    }
Some compilers treat this as ambiguous; others prefer f(const T&). The question turns out to revolve around whether 14.8.2.1 [temp.deduct.call] paragraph 2 says what it ought to regarding the removal of cv-qualifiers and reference modifiers from template function parameters in doing type deduction.

John Spicer: The partial ordering rules as originally proposed specified that, for purposes of comparing parameter types, you remove a top level reference, and after having done that you remove top level qualifiers. This is not what is actually in the IS however. The IS says that you remove top level qualifiers and then top level references.

The original rules were intended to prefer f(A<T>) over f(const T&).

Rationale (10/99): The Standard correctly reflects the intent of the Committee.

(October 2002) This is resolved by issue 214.




211. Constructors should not be allowed to return normally after an exception

Section: 15  [except]     Status: NAD     Submitter: Bruce Mellows     Date: 8 Mar 2000

The grammar should be changed so that constructor function-try-blocks must end with a throw-expression.

Rationale (04/00):

No change is needed. It is already the case that flowing off the end of a function-try-block of a constructor rethrows the exception, and return statements are prohibited in constructor function-try-blocks (15.3 [except.handle] paragraphs 15-16.




104. Destroying the exception temp when no handler is found

Section: 15.1  [except.throw]     Status: NAD     Submitter: Jonathan Schilling     Date: 21 Mar 1999

Questions regarding when a throw-expression temporary object is destroyed.

Section 15.1 [except.throw] paragraph 4 describes when the temporary is destroyed when a handler is found. But what if no handler is found:

    struct A {
        A() { printf ("A() \n"); }
        A(const A&) { printf ("A(const A&)\n"); }
        ~A() { printf ("~A() \n"); }
    };

    void t() { exit(0); }

    int main() {
        std::set_terminate(t);
        throw A();
    }
Does A::~A() ever execute here? (Or, in case two constructions are done, are there two destructions done?) Is it implementation-defined, analogously to whether the stack is unwound before terminate() is called (15.3 [except.handle] paragraph 9)?

Or what if an exception specification is violated? There are several different scenarios here:

    int glob = 0; // or 1 or 2 or 3

    struct A {
        A() { printf ("A() \n"); }
        A(const A&) { printf ("A(const A&)\n"); }
        ~A() { printf ("~A() \n"); }
    };

    void u() {
        switch (glob) {
        case 0:  exit(0);
        case 1:  throw "ok";
        case 2:  throw 17;
        default: throw;
        }
    }

    void foo() throw(const char*, std::bad_exception) {
        throw A();
    }

    int main() {
        std::set_unexpected(u);
        try {
            foo();
        }
        catch (const char*) { printf("in handler 1\n"); }
        catch (std::bad_exception) { printf("in handler 2\n"); }
    }
The case where u() exits is presumably similar to the terminate() case. But in the cases where the program goes on, A::~A() should be called for the thrown object at some point. But where does this happen? The standard doesn't really say. Since an exception is defined to be "finished" when the unexpected() function exits, it seems to me that is where A::~A() should be called — in this case, as the throws of new (or what will become new) exceptions are made out of u(). Does this make sense?

Rationale (10/99): Neither std::exit(int) nor std::abort() destroys temporary objects, so the exception temporary is not destroyed when no handler is found. The original exception object is destroyed when it is replaced by an unexpected() handler. The Standard is sufficiently clear on these points.




308. Catching exceptions with ambiguous base classes

Section: 15.3  [except.handle]     Status: NAD     Submitter: Sergey P. Derevyago     Date: 04 Sep 2001

15.3 [except.handle] paragraph 3 contains the following text:

A handler is a match for a throw-expression with an object of type E if

I propose to alter this text to allow to catch exceptions with ambiguous public base classes by some of the public subobjects. I'm really sure that if someone writes:

    try {
        // ...
    }
    catch (Matherr& m) {
        // ...
    }
he really wants to catch all Matherrs rather than to allow some of the Matherrs to escape:
    class SomeMatherr : public Matherr { /* */ };
    struct TrickyBase1 : public SomeMatherr { /* */ };
    struct TrickyBase2 : public SomeMatherr { /* */ };
    struct TrickyMatherr : public TrickyBase1, TrickyBase2 { /* */ };

According to the standard TrickyMatherr will leak through the catch (Matherr& m) clause. For example:

    #include <stdio.h>

    struct B {};
    struct B1 : B {};
    struct B2 : B {};
    struct D : B1, B2 {};  // D() has two B() subobjects

    void f() { throw D(); }

    int main()
    {
     try { f(); }
     catch (B& b) { puts("B&"); }  // passed
     catch (D& d) { puts("D&"); }  // really works _after_ B&!!!
    }

Also I see one more possible solution: to forbid objects with ambiguous base classes to be "exceptional objects" (for example Borland C++ goes this way) but it seems to be unnecessary restrictive.

Notes from the 10/01 meeting:

The Core Working Group did not feel this was a significant problem. Catching either of the ambiguous base classes would be surprising, and giving an error on throwing an object that has an ambiguous base class would break existing code.




593. Falling off the end of a destructor's function-try-block handler

Section: 15.3  [except.handle]     Status: NAD     Submitter: Alisdair Meredith     Date: 30 August 2006

If control reaches the end of handler in a destructor's function-try-block, the exception is rethrown (15.3 [except.handle] paragraph 15). Because of the danger of destructors that throw exceptions, would it be better to treat this case as an implicit return; statement, as in a function body? There could be a transitional period, perhaps using conditionally-supported behavior or the like, before mandating the change.

Rationale (October, 2006):

The CWG felt that the current behavior is clearly specified and reflects the intention of the Committee at the time the rules were adopted. Possible changes to these rules should be pursued through the Evolution Working Group.




346. Typo in 15.4

Section: 15.4  [except.spec]     Status: NAD     Submitter: Lois Goldthwaite     Date: 18 March 2002

15.4 [except.spec] paragraph 13 contains the following text. I believe 'implicitLY' marked below should be replaced with 'implicit.'

An implicitly declared special member function (clause 12 [special]) shall have an exception-specification. If f is an implicitly declared default constructor, copy constructor, destructor, or copy assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f's implicitly definition; f shall allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions if every function it directly invokes allows no exceptions. [Example:

    struct A {
        A();
        A(const A&) throw();
        ~A() throw(X);
    };
    struct B {
        B() throw();
        B(const B&) throw();
        ~B() throw(Y);
    };
    struct D : public A, public B {
            //  Implicit declaration of  D::D();
            //  Implicit declaration of  D::D(const   D&)   throw();
            //  Implicit declaration of  D::~D()   throw   (X,Y);
    };

Furthermore, if A::~A() or B::~B() were virtual, D::~D() would not be as restrictive as that of A::~A, and the program would be ill-formed since a function that overrides a virtual function from a base class shall have an exception-specification at least as restrictive as that in the base class. ]

The example code shows structs whose destructors have exception specifications which throw certain types. There is no defect here, but it doesn't sit well with our general advice elsewhere that destructors should not throw. I wish I could think of some other way to illustrate this section.

Notes from October 2002 meeting:

This was previously resolved by an editorial change.




37. When is uncaught_exception() true?

Section: 15.5.3  [except.uncaught]     Status: NAD     Submitter: Daveed Vandevoorde     Date: 10 Aug 1998

The term "throw exception" seems to sometimes refer to an expression of the form "throw expr" and sometimes just to the "expr" portion thereof.

As a result it is not quite clear to me whether when "uncaught_exception()" becomes true: before or after the temporary copy of the value of "expr".

Is there a definite consensus about that?

Rationale: The standard is sufficiently clear; the phrase "to be thrown" indicates that the throw itself (which includes the copy to the temporary object) has not yet begun. The footnote in 15.5.1 [except.terminate] paragraph 1 reinforces this ordering.

See also issue 475.




584. Unions and aliasing

Section: 3.10  [basic.lval]     Status: NAD     Submitter: comp.std.c++     Date: 10 June 2006

The C++ standard says in 3.10 [basic.lval], in paragraph 15:

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:

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.




50. Converting pointer to incomplete type to same type

Section: 3.2  [basic.def.odr]     Status: NAD     Submitter: Steve Adamczyk     Date: 13 Oct 1998

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.




42. Redefining names from base classes

Section: 3.3.6  [basic.scope.class]     Status: NAD     Submitter: Steve Clamage     Date: 15 Sep 1998

Consider this code:

    struct Base {
        enum { a, b, c, next };
    };

    struct Derived : public Base {
        enum { d = Base::next, e, f, next };
    };

The idea is that the enumerator "next" in each class is the next available value for enumerators in further derived classes.

If we had written

    enum { d = next, e, f, next };

I think we would run afoul of 3.3.6 [basic.scope.class] :
A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.
But in the original code, we don't have an unqualified "next" that refers to anything but the current scope. I think the intent was to allow the code, but I don't find the wording clear on on that point.

Is there another section that makes it clear whether the original code is valid? Or am I being obtuse? Or should the quoted section say "An unqualified name N used in a class ..."?

Rationale (04/99): It is sufficiently clear that "name" includes qualified names and hence the usual lookup rules make this legal.




231. Visibility of names after using-directives

Section: 3.4.1  [basic.lookup.unqual]     Status: NAD     Submitter: Jörg Barfurth     Date: 31 May 2000

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.5 [basic.scope.namespace] paragraph 1.




91. A union's associated types should include the union itself

Section: 3.4.2  [basic.lookup.argdep]     Status: NAD     Submitter: John Spicer     Date: 2 Feb 1999

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.




384. Argument-dependent lookup and operator functions

Section: 3.4.2  [basic.lookup.argdep]     Status: NAD     Submitter: Herb Sutter     Date: 18 Sept 2002

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:

  1. Should the enclosing class(es) of a class, and their template argument lists (if any) contribute to the associated classes/namespaces for ADL?
  2. Should the template argument lists of base classes contribute to the associated classes/namespaces for ADL?

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.




635. Names of constructors and destructors of templates

Section: 3.4.3.1  [class.qual]     Status: NAD     Submitter: Martin Sebor     Date: 23 May 2007

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.6.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.




132. Local types and linkage

Section: 3.5  [basic.link]     Status: NAD     Submitter: Daveed Vandevoorde     Date: 25 June 1999

3.5 [basic.link] paragraph 8 says,

A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.2 [basic.scope.local] )) shall not be used to declare an entity with linkage.
This wording does not, but should, prohibit use of an unnamed local type in the declaration of an entity with linkage. For example,
    void f() {
        extern struct { } x;  // currently allowed
    }

Proposed resolution: Change the text in 3.5 [basic.link] paragraph 8 from:

A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.2 [basic.scope.local])) shall not be used to declare an entity with linkage.
to:
A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.2 [basic.scope.local])) or an unnamed type shall not be used to declare an entity with linkage.
In section 3.5 [basic.link] paragraph 8, add to the example, before the closing brace of function f:
extern struct {} x;    // ill-formed

Rationale (10/00): The proposed change would have introduced an incompatibility with the C language. For example, the global declaration

    static enum { A, B, C } abc;

represents an idiom that is used in C but would be prohibited under this resolution.




269. Order of initialization of multiply-defined static data members of class templates

Section: 3.6.2  [basic.start.init]     Status: NAD     Submitter: Andrei Iltchenko     Date: 8 Feb 2001

According to 3.2 [basic.def.odr] paragraph 5, it is possible for a static data member of a class template to be defined more than once in a given program provided that each such definition occurs in a different translation unit and the ODR is met.

Now consider the following example:

src1.cpp:

    #include <iostream>

    int  initializer()
    {
       static int   counter;
       return  counter++;
    }

    int   g_data1 = initializer();

    template<class T>
    struct  exp  {
       static int   m_data;
    };
    template<class T>
    int  exp<T>::m_data = initializer();

    int   g_data2 = initializer();
    extern int   g_data3;

    int  main()
    {
       std::cout << exp<char>::m_data << ", " << g_data1 << ", "
	  << g_data2 << ", " << g_data3 << std::endl;
       return  0;
    }

src2.cpp:

    extern int  initializer();
    int  g_data3 = initializer();

    template<class T>
    struct  exp  {
       static int   m_data;
    };
    template<class T>
    int  exp<T>::m_data = initializer();

    void  func()
    {
      exp<char>::m_data++;
    }

The specialization exp<char>::m_data is implicitly instaniated in both translation units, hence (14.7.1 [temp.inst] paragraph 1) its initialization occurs. And for both definitions of exp<T>::m_data the ODR is met. According to 3.6.2 [basic.start.init] paragraph 1:

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.

But for exp<T>::m_data we have two definitions. Does it mean that both g_data1 and g_data3 are guaranteed to be dynamically initialized before exp<char>::m_data?

Suggested Resolution: Insert the following sentence before the last two sentences of 3.2 [basic.def.odr] paragraph 5:

In the case of D being a static data member of a class template the following shall also hold:

Notes from 10/01 meeting:

It was decided that this issue is not linked to issue 270 and that there is no problem, because there is only one instantiation (see 2.1 [lex.phases] paragraph 8).




465. May constructors of global objects call exit()?

Section: 3.6.2  [basic.start.init]     Status: NAD     Submitter: Matt Austern     Date: 26 Feb 2004

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.




234. Reuse of base class subobjects

Section: 3.8  [basic.life]     Status: NAD     Submitter: Bill Wade     Date: 28 Jun 2000

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.




290. Should memcpy be allowed into a POD with a const member?

Section: 3.9  [basic.types]     Status: NAD     Submitter: Garry Lancaster     Date: 12 Jun 2001

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].




627. Values behaving as types

Section: 3.9.1  [basic.fundamental]     Status: NAD     Submitter: Gennaro Prota     Date: 15 March 2007

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.




456. Is initialized const int or const bool variable a null pointer constant?

Section: 4.10  [conv.ptr]     Status: NAD     Submitter: Lloyd Lewins     Date: 31 Jan 2004

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.




303. Integral promotions on bit-fields

Section: 4.5  [conv.prom]     Status: NAD     Submitter: Kiril Avdeiv     Date: 24 Jul 2001

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.




566. Conversion of negative floating point values to integer type

Section: 4.9  [conv.fpint]     Status: NAD     Submitter: Seungbeom Kim     Date: 13 March 2006

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:

  1. Replace “truncates” with “truncates toward zero.”

  2. 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.




71. Incorrect cross reference

Section: 5  [expr]     Status: NAD     Submitter: Neal Gafter     Date: 15 Oct 1998

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.




97. Use of bool constants in integral constant expressions

Section: 5.19  [expr.const]     Status: NAD     Submitter: Andy Koenig     Date: 18 Feb 1999

Consider:

    int* p = false;         // Well-formed?
    int* q = !1;            // What about this?
>From 3.9.1 [basic.fundamental] paragraph 6: "As described below, bool values behave as integral types."

From 4.10 [conv.ptr] paragraph 1: "A null pointer constant is an integral constant expression rvalue of integer type that evaluates to zero."

From 5.19 [expr.const] paragraph 1: "An integral constant-expression can involve only literals, enumerators, const variables or static members of integral or enumeration types initialized with constant expressions, ..."

In 2.13.1 [lex.icon] : No mention of true or false as an integer literal.

From 2.13.5 [lex.bool] : true and false are Boolean literals.

So the definition of q is certainly valid, but the validity of p depends on how the sentence in 5.19 [expr.const] is parsed. Does it mean

Or does it mean Or something else?

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.




487. Operator overloading in constant expressions

Section: 5.19  [expr.const]     Status: NAD     Submitter: Steve Adamczyk     Date: 24 Nov 2004

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.




610. Computing the negative of 0U

Section: 5.3.1  [expr.unary.op]     Status: NAD     Submitter: comp.std.c++     Date: 21 December 2006

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.



31. Looking up new/delete

Section: 5.3.4  [expr.new]     Status: NAD     Submitter: Daveed Vandevoorde     Date: 23 Jun 1998

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] ).

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. ]

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".

Rationale:

Paragraphs 16 through 18 are sufficiently correct and unambiguous as written.




130. Sequence points and new-expressions

Section: 5.3.4  [expr.new]     Status: NAD     Submitter: Herb Sutter     Date: 20 June 1999

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 However, 5.3.4 [expr.new] paragraph 17 seems to require the deallocation of the storage for an object only if part of the initialization of that object terminates with an exception. Given the ordering above, this specification would appear to allow the memory for the T2 object to be leaked if the T1 constructor throws an exception.

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.




55. Adding/subtractin