Topic: Is this valid C++?


Author: pln@egret1.stanford.edu (Patrick L. Nolan)
Date: 1999/04/13
Raw View
The small program attached below will not compile with egcs 1.1.2.  I have
consulted several local "experts", and they all think it looks like valid
code.  The error message is

$ g++ -Wall shortfoo.cxx
shortfoo.cxx: In function `int main()':
shortfoo.cxx:35: `operator *(const int &, const int &)' must have an argument
of class or enumerated type

-----------------------------------------------------

#include <iostream>

template <class T>
class base {
public:
    T& operator *= (const int & rhs) {
 base_data *= rhs;
 return *static_cast<T*>(this);
    }
    T operator * (const int & rhs) const {
 T r(*static_cast<const T*>(this));
 r *= rhs;
 return r;
    }
 private:
    int base_data;
 protected:
    base<T>(int y=0) : base_data(y) {};
    const int & get_data() const { return base_data;}
};

template <class T>  // It compiles OK if I comment out these 4 lines.
T operator * (const int & l, const T & r) {
    return r*l;
}

class derived: public base<derived> {
 public:
    derived(const int  y=0) : base<derived>(y) {};
    int val() const {return get_data();}
};

main() {
    derived blort(1);
    derived wah = blort * 5; //  This is line #35.  Error here.
    cout << wah.val() << endl;
}
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Martin von Loewis <loewis@informatik.hu-berlin.de>
Date: 1999/04/13
Raw View
pln@egret1.stanford.edu (Patrick L. Nolan) writes:

> The small program attached below will not compile with egcs 1.1.2.  I have
> consulted several local "experts", and they all think it looks like valid
> code.

I'd agree that this is well-formed. Apparently, g++ instantiates the
template with int when determining 'viable' functions, and then
complains. I think it should just avoid instantiating the template if
the result would not be a proper operator.

Anyway, the obvious work-around is to write

  derived wah = blort.operator*(5);

Hope this helps,
Martin
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Gabriel Dos_Reis <gdosreis@korrigan.inria.fr>
Date: 1999/04/13
Raw View
pln@egret1.stanford.edu (Patrick L. Nolan) writes:

| The small program attached below will not compile with egcs 1.1.2.  I have
| consulted several local "experts", and they all think it looks like valid
| code.  The error message is
|
| $ g++ -Wall shortfoo.cxx
| shortfoo.cxx: In function `int main()':
| shortfoo.cxx:35: `operator *(const int &, const int &)' must have an argument
| of class or enumerated type
|
| -----------------------------------------------------
|
| #include <iostream>

[snip code, I seem to be familiar with :-)]

[...]

The program is well formed. EGCS has some problems when performing overload
resolution. It is trying to *instanciate* template in the process of
finding the best viable function(s) and gives up at the first ill-formed
instanciation instead of disposing of that particular instanciation. Sigh.

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1999/04/13
Raw View
Patrick L. Nolan wrote:
>
> The small program attached below will not compile with egcs 1.1.2.  I have
> consulted several local "experts", and they all think it looks like valid
> code.  The error message is
>
> $ g++ -Wall shortfoo.cxx
> shortfoo.cxx: In function `int main()':
> shortfoo.cxx:35: `operator *(const int &, const int &)' must have an argument
> of class or enumerated type

....
> template <class T>  // It compiles OK if I comment out these 4 lines.
> T operator * (const int & l, const T & r) {
>     return r*l;
> }

You've created a template with a couple of nasty problems. First of all,
if 'T' is a standard type, it defines an illegal override. I don't see
any place where you've done that directly, but the logic of your code is
messy, and I wouldn't be surprised if you've done it indirectly. That
would explain the error message you got. Secondly, if T were any type
that promotes to an 'int', if this override were legal, it would define
an infinite recursion.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Ron Natalie <ron@sensor.com>
Date: 1999/04/13
Raw View
Patrick L. Nolan wrote:
>
> shortfoo.cxx: In function `int main()':
> shortfoo.cxx:35: `operator *(const int &, const int &)' must have an argument
> of class or enumerated type
>

The compiler is correct on this.  Operator overloads need at
least one user defined (class or enum) type.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Gabriel Dos_Reis <gdosreis@korrigan.inria.fr>
Date: 1999/04/13
Raw View
James Kuyper <kuyper@wizard.net> writes:

| Patrick L. Nolan wrote:
| >
| > The small program attached below will not compile with egcs 1.1.2.  I have
| > consulted several local "experts", and they all think it looks like valid
| > code.  The error message is
| >
| > $ g++ -Wall shortfoo.cxx
| > shortfoo.cxx: In function `int main()':
| > shortfoo.cxx:35: `operator *(const int &, const int &)' must have an argument
| > of class or enumerated type
|
| ....
| > template <class T>  // It compiles OK if I comment out these 4 lines.
| > T operator * (const int & l, const T & r) {
| >     return r*l;
| > }
|
| You've created a template with a couple of nasty problems. First of all,
| if 'T' is a standard type, it defines an illegal override. I don't see

Why override? If implicit instanciation, the template version will not
be considered (when T is a builtin type).

| any place where you've done that directly, but the logic of your code is
| messy,

Unless I'm mistaken I think he is trying to implement "compile-time
virtual function".

| ... and I wouldn't be surprised if you've done it indirectly. That
| would explain the error message you got.

I've been able to reproduce the error using the program he
posted. See Martin's reply (and mine).

| ... Secondly, if T were any type
| that promotes to an 'int', if this override were legal, it would define
| an infinite recursion.

Could you elablorate, please? I agree with the general point you're
tying to make but in this specific case, it doesn't matter, IMHO.

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Gabriel Dos_Reis <gdosreis@korrigan.inria.fr>
Date: 1999/04/13
Raw View
Ron Natalie <ron@sensor.com> writes:

| Patrick L. Nolan wrote:
| >
| > shortfoo.cxx: In function `int main()':
| > shortfoo.cxx:35: `operator *(const int &, const int &)' must have an argument
| > of class or enumerated type
| >
|
| The compiler is correct on this.  Operator overloads need at
| least one user defined (class or enum) type.

But he didn't define anything like operator* (const int&, const int&),
nor did he instanciate any such thing.

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1999/04/13
Raw View
Gabriel Dos_Reis wrote:
>
> James Kuyper <kuyper@wizard.net> writes:
>
> | Patrick L. Nolan wrote:
...
> | ....
> | > template <class T>  // It compiles OK if I comment out these 4 lines.
> | > T operator * (const int & l, const T & r) {
> | >     return r*l;
> | > }
...
> | ... Secondly, if T were any type
> | that promotes to an 'int', if this override were legal, it would define
> | an infinite recursion.
>
> Could you elablorate, please? I agree with the general point you're
> tying to make but in this specific case, it doesn't matter, IMHO.

If T==int were allowed, then the line that says 'return r*l;' would be
equivalent to

 return operator*(r,l);

Hence infinite recursion.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Gabriel Dos_Reis <gdosreis@korrigan.inria.fr>
Date: 1999/04/15
Raw View
Martin von Loewis <loewis@informatik.hu-berlin.de> writes:

| James.Kanze@dresdner-bank.com writes:
|
| > No argument about that.  All that means, however, is that *if* the
| > template were instantiated for int, it would be illegal.  That's a big
| > if -- in the given code, I couldn't find any instantiation for anything
| > but a user defined type.
|
| I think that the template is indeed instantiated with an <int> during
| template argument deduction, since argument deduction succeeds for an
| <int> template parameter ([temp.deduct.call]). Now, the compiler needs
| to instantiate the template to perform overload resolution
| ([temp.over]/1).

I think that paragraph needs clarification.

---
1 A  function  template can be overloaded either by (non-template) func-
  tions of its name or by (other) function templates of the  same name.
  When  a  call to that name is written (explicitly, or implicitly using
  the operator notation), template  argument  deduction (_temp.deduct_)
  and  checking of any explicit template arguments (_temp.arg_) are per-
  formed for each function template to find the template argument values
  (if any) that can be used with that function template to instantiate a
  function template specialization that can be  invoked  with  the call
  arguments.   For each function template, if the argument deduction and
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  checking succeeds, the template-arguments  (deduced  and/or explicit)
  ^^^^^^^^^^^^^^^^^^

  are  used  to  instantiate  a  single function template specialization
  which is added to the candidate functions set to be used  in overload
  resolution.   If,  for  a  given function template, argument deduction
  fails, no such function is added to the set of candidate functions for
  that  template.   The complete set of candidate functions includes all
  the function templates instantiated in this way and all  of  the non-
  template overloaded functions of the same name.  The function template

  specializations are treated like any other functions in the remainder
  of    overload    resolution,    except   as   explicitly   noted in
  _over.match.best_.8)
---


Is the checking done for deduced template arguments? If yes, then
the program should not be rejected. If no, then EGCS behaviour is
standard conforming although surprising. Indeed as implicit
instantiation is done at the discretion of the compiler, it seems
natural that only those deduced template arguments that lead to
well-formed construct should be used in implicit instantiation. If
checking fails then no attempt is done to do instantiation  and no
corresponding function should be added to the set of candidate
functions. That would remove one more exceptional case.

Would anyone that participated in the template chapter elaboration
give me any clarification and explain why deduced template arguments
would not be checked?

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Gabriel Dos_Reis <gdosreis@korrigan.inria.fr>
Date: 1999/04/13
Raw View
James Kuyper <kuyper@wizard.net> writes:

| Gabriel Dos_Reis wrote:
| >
| > James Kuyper <kuyper@wizard.net> writes:
| >
| > | Patrick L. Nolan wrote:
| ...
| > | ....
| > | > template <class T>  // It compiles OK if I comment out these 4 lines.
| > | > T operator * (const int & l, const T & r) {
| > | >     return r*l;
| > | > }
| ...
| > | ... Secondly, if T were any type
| > | that promotes to an 'int', if this override were legal, it would define
      ^^^^^^^^^^^^^^^^^^^^^^^^^

| > | an infinite recursion.
| >
| > Could you elablorate, please? I agree with the general point you're
| > tying to make but in this specific case, it doesn't matter, IMHO.
|
| If T==int were allowed, then the line that says 'return r*l;' would be
| equivalent to
|
|  return operator*(r,l);
|
| Hence infinite recursion.

But during argument deduction, promotions are not considered. Right?

And if r and l actually happen to be of type const int&, then a decent
compiler will not intantiate any user-defined operator.
So I'm still not convinced.

--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James.Kanze@dresdner-bank.com
Date: 1999/04/13
Raw View
In article <371347F8.25226B72@sensor.com>,
  Ron Natalie <ron@sensor.com> wrote:
> Patrick L. Nolan wrote:
> >
> > shortfoo.cxx: In function `int main()':
> > shortfoo.cxx:35: `operator *(const int &, const int &)' must have an
argument
> > of class or enumerated type
> >
>
> The compiler is correct on this.  Operator overloads need at
> least one user defined (class or enum) type.

No argument about that.  All that means, however, is that *if* the
template were instantiated for int, it would be illegal.  That's a big
if -- in the given code, I couldn't find any instantiation for anything
but a user defined type.

--
James Kanze                         mailto: James.Kanze@dresdner-bank.com
Conseils en informatique orient=E9e objet/
                        Beratung in objekt orientierter Datenverarbeitung
Ziegelh=FCttenweg 17a, 60598 Frankfurt, Germany  Tel. +49 (069) 63 19 86 =
27

-----------=3D=3D Posted via Deja News, The Discussion Network =3D=3D----=
------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own  =
 =20
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Martin von Loewis <loewis@informatik.hu-berlin.de>
Date: 1999/04/13
Raw View
James.Kanze@dresdner-bank.com writes:

> No argument about that.  All that means, however, is that *if* the
> template were instantiated for int, it would be illegal.  That's a big
> if -- in the given code, I couldn't find any instantiation for anything
> but a user defined type.

I think that the template is indeed instantiated with an <int> during
template argument deduction, since argument deduction succeeds for an
<int> template parameter ([temp.deduct.call]). Now, the compiler needs
to instantiate the template to perform overload resolution
([temp.over]/1).

The question is whether this implicit instantiation causes the program
to be ill-formed. Since explicit instantiation may lead to ill-formed
constructs, I'd expect that implicit instantiation may cause
ill-formedness as well.

So where is the wording in the standard that clarifies this question?

Martin
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1999/04/14
Raw View
Gabriel Dos_Reis wrote:
>
> James Kuyper <kuyper@wizard.net> writes:
>
> | Gabriel Dos_Reis wrote:
> | >
> | > James Kuyper <kuyper@wizard.net> writes:
> | >
> | > | Patrick L. Nolan wrote:
> | ...
> | > | ....
> | > | > template <class T>  // It compiles OK if I comment out these 4 lines.
> | > | > T operator * (const int & l, const T & r) {
> | > | >     return r*l;
> | > | > }
> | ...
> | > | ... Secondly, if T were any type
> | > | that promotes to an 'int', if this override were legal, it would define
>       ^^^^^^^^^^^^^^^^^^^^^^^^^
>
> | > | an infinite recursion.
> | >
> | > Could you elablorate, please? I agree with the general point you're
> | > tying to make but in this specific case, it doesn't matter, IMHO.
> |
> | If T==int were allowed, then the line that says 'return r*l;' would be
> | equivalent to
> |
> |       return operator*(r,l);
> |
> | Hence infinite recursion.
>
> But during argument deduction, promotions are not considered. Right?

Promotion isn't the key issue; the problem I was worried about is if T
is 'int'.

> And if r and l actually happen to be of type const int&, then a decent
> compiler will not intantiate any user-defined operator.
> So I'm still not convinced.

Operator overload resolution occurs only when an operand expression
originally has class or enumeration type, and therefore would ordinarily
not resolve to operator+(const int&, const int&). However,if T is a type
with a conversion leading to 'const int&', then overload resolution can
lead to that operator. If it does, then it can be resolved to a
user-written function, and I don't see how an implementation could be
allowed to not use that template to create the function. See section
13.6, p1.

However, in such a case, when operator+(const int&, const int&) is
instantiated, evaluating the 'return' statement doesn't involve a
user-defined type, there's no overload resolution, and thus the built-in
operator is called, terminating what I'd thought was an infinite
recursion. I was mistaken, but for slightly more complicated reasons
than the one you gave.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]