Topic: Name injection and the ODR


Author: Pulkkinen Esa <esap@cs.tut.fi>
Date: 1997/03/08
Raw View
Consider the following example:

template <class T>
class SomeCategory
{
public:
  // A definition of a friend function depending on the template
  // argument defined in the friend declaration:
  inline friend void doSomething(const T& x) { ... }
};

template <class T>
class AnotherCategory
{
public:
  inline friend void doSomething(const T& x) { ... }
};

Now assume I'd want to do something like this:

class MyClass
: public AnotherCategory<MyClass>,
  public SomeCategory<MyClass>
{
};

Now, how would I disambiguate the doSomething function so I could decide
which one to call?

For example, you can't do this [in MyClass]:

  friend void doSomething(const MyClass &x)
  { AnotherCategory<MyClass>::doSomething(x);
    SomeCategory<MyClass>::doSomething(x);
  }

Or is this case simply a violation of the ODR and thus a non-required
diagnostic, and I have to be careful not to instantiate two classes
in a way that the instantiations of the friends overlap?

Also, what happens if the function defined in a friend declaration
in a class template doesn't depend on the template argument:

template <class T>
class X
{
  friend void f() { T x; x.print(); }
};

Does this make the program invalid (according to the ODR) if the class
is instantiated more than once? (I.e. can implicit template
instantiation cause the program be ill-formed according to the ODR?)
How about if the template has several arguments, and the function only
depends on some of the arguments? (I'd assume the same answer as the
previous question). Could the compiler be required to give diagnostic
on such cases?
--
   Esa Pulkkinen                        | C++ programmers do it virtually
   E-Mail:  esap@cs.tut.fi              | everywhere with class, resulting
   WWW   :  http://www.cs.tut.fi/~esap/ | in multiple inheritance.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Marcelo Cantos <marcelo@mds.rmit.edu.au>
Date: 1997/03/08
Raw View
Pulkkinen Esa <esap@cs.tut.fi> writes:

> class MyClass
> : public AnotherCategory<MyClass>,
>   public SomeCategory<MyClass>
> {};
>
> Now, how would I disambiguate the doSomething function so I could decide
> which one to call?
>
> For example, you can't do this [in MyClass]:
>
>   friend void doSomething(const MyClass &x)
>   { AnotherCategory<MyClass>::doSomething(x);
>     SomeCategory<MyClass>::doSomething(x);
>   }

  friend void doSomething(const MyClass &x)
  { x.AnotherCategory<MyClass>::doSomething();
    x.SomeCategory<MyClass>::doSomething();
  }


--
______________________________________________________________________
Marcelo Cantos, Research Assistant             marcelo@mds.rmit.edu.au
Multimedia Database Systems Group, RMIT__/_      _  Tel 61-3-9282-2497
723 Swanston St, Carlton VIC 3053    Aus/ralia ><_> Fax 61-3-9282-2490
                                       /
Acknowledgements: errors - me; wisdom - God; funding - RMIT
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Pulkkinen Esa <esap@cs.tut.fi>
Date: 1997/03/09
Raw View
> Pulkkinen Esa <esap@cs.tut.fi> writes:
[both AnotherCategory<T> and
SomeCategory<T> define (and not just declare) a friend function with the
same name, and it's multiply derived into MyClass]

> > Now, how would I disambiguate the doSomething function so I could decide
> > which one to call?
> > For example, you can't do this [in MyClass]:
> >   friend void doSomething(const MyClass &x)
> >   { AnotherCategory<MyClass>::doSomething(x);
> >     SomeCategory<MyClass>::doSomething(x);
> >   }

Marcelo Cantos <marcelo@mds.rmit.edu.au> writes:
>   friend void doSomething(const MyClass &x)
>   { x.AnotherCategory<MyClass>::doSomething();
>     x.SomeCategory<MyClass>::doSomething();
>   }

I think that doesn't work since doSomething is not a member
function. doSomething() is a friend of AnotherCategory and SomeCategory,
so at least according to my interpretation, the draft disallows this.
Could you provide the location in the draft that allows this syntax for
calling a friend function? (I couldn't find it!) Anyway, I find this
syntax very confusing, because you don't usually call global (namespace
scope) functions that way.
--
   Esa Pulkkinen                        | C++ programmers do it virtually
   E-Mail:  esap@cs.tut.fi              | everywhere with class, resulting
   WWW   :  http://www.cs.tut.fi/~esap/ | in multiple inheritance.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Marcelo Cantos <marcelo@mds.rmit.edu.au>
Date: 1997/03/09
Raw View
Pulkkinen Esa <esap@cs.tut.fi> writes:

> > Pulkkinen Esa <esap@cs.tut.fi> writes:
> [both AnotherCategory<T> and
> SomeCategory<T> define (and not just declare) a friend function with the
> same name, and it's multiply derived into MyClass]

This is wrong.  Friend functions cannot be defined separately for two
classes, since their scope is global.  To make the friend belong to
the class, it should instead be a member function.

>
> > > Now, how would I disambiguate the doSomething function so I could decide
> > > which one to call?
> > > For example, you can't do this [in MyClass]:
> > >   friend void doSomething(const MyClass &x)
> > >   { AnotherCategory<MyClass>::doSomething(x);
> > >     SomeCategory<MyClass>::doSomething(x);
> > >   }
>
> Marcelo Cantos <marcelo@mds.rmit.edu.au> writes:
> >   friend void doSomething(const MyClass &x)
> >   { x.AnotherCategory<MyClass>::doSomething();
> >     x.SomeCategory<MyClass>::doSomething();
> >   }
>
> I think that doesn't work since doSomething is not a member
> function. doSomething() is a friend of AnotherCategory and SomeCategory,
> so at least according to my interpretation, the draft disallows this.
> Could you provide the location in the draft that allows this syntax for
> calling a friend function? (I couldn't find it!) Anyway, I find this
> syntax very confusing, because you don't usually call global (namespace
> scope) functions that way.

My apologies; I missed the 'friend' keyword.  Your problem is that the
two friend declarations are a reference to the same function, not two
functions belonging to their respective classes.  Friends don't belong
to a class, they simply have special access to the class.  In light of
all this, using explicit scoping is meaningless since the friend
function itself is simply a global function, one that can directly
manipulate both classes.

What exactly are you trying to do?


--
______________________________________________________________________
Marcelo Cantos, Research Assistant             marcelo@mds.rmit.edu.au
Multimedia Database Systems Group, RMIT__/_      _  Tel 61-3-9282-2497
723 Swanston St, Carlton VIC 3053    Aus/ralia ><_> Fax 61-3-9282-2490
                                       /
Acknowledgements: errors - me; wisdom - God; funding - RMIT
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Pulkkinen Esa <esap@cs.tut.fi>
Date: 1997/03/11
Raw View
[This got a little lengthy]
Marcelo Cantos <marcelo@mds.rmit.edu.au> writes:
> > > Pulkkinen Esa <esap@cs.tut.fi> writes:
> > [both AnotherCategory<T> and
> > SomeCategory<T> define (and not just declare) a friend function with the
> > same name, and it's multiply derived into MyClass]
>
> This is wrong.  Friend functions cannot be defined separately for two
> classes, since their scope is global.  To make the friend belong to
> the class, it should instead be a member function.

Agreed. The question is whether the compiler can and/or is required to
diagnose this as an error.

[Snip]
> functions belonging to their respective classes.  Friends don't belong
> to a class, they simply have special access to the class.  In light of
> all this, using explicit scoping is meaningless since the friend
> function itself is simply a global function, one that can directly
> manipulate both classes.
>
> What exactly are you trying to do?

Assume I'm defining an function-structure category (see Scientific and
Engineering C++ by Barton&Nackman) using friends like this:

template <class T>
class OutputCategory
{
public:
  friend ostream &operator<<(ostream& o,const T& x)
  { x.print(o); return *this; }
  friend void print(ostream& o,const T& x)
  { x.print(o); }
  // ...
};

You can use this to provide implementations for global operator<< and
print functions that just call the (perhaps virtual) member function
print in the class 'T' that's provided as a template argument. For
example:

class MyComplexClass : public OutputCategory<MyComplexClass>
{
public:
  virtual void print(ostream& o);
  // Automatically defines ::operator<<(ostream&,const MyComplexClass&)
  // and ::print(ostream&, const MyComplexClass&) because this class
  // is derived from (and so instantiates) OutputCategory<MyComplexClass>.
};

Then consider I'd for some reason want to call different function from
print (For example, named 'output').

So I could define another function-structure category:

template <class T>
class AnotherOutputCategory
{
public:
  friend void print(ostream& o,const T& x)
  { x.output(o); }
  // ... some other functions, mostly not overlapping with those
  //  in OutputCategory<T>.
};

Now, both OutputCategory<T> and AnotherOutputCategory<T> are usable in
themselves, and perhaps even useful, and can be used to define
global overloaded functions for the library user's classes
automatically.

But if you accidentally instantiate both classes with the same template
argument, you will get a violation of the ODR. This is very undesirable
because

  a) You can accidentally get into this situation.

  b) The compiler isn't required to diagnose this (or is it? if so,
     please point out where this is stated in the draft!). I think
     this doesn't require a diagnostic [basic.def.odr]/3. After all,
     you can instantiate both separately in different
     translation units (But gcc didn't give a diagnostic
     for it even if I did it in the same translation unit -
     this is where I started to get worried about it - OTOH Sun's compiler
     did complain about multiply defined 'body' for the function if the
     instantiations happened to be in the same translation unit...)

  c) The problem only occurs when the classes are instantiated - i.e.
     the library designers aren't necessarily aware of the fact.

  d) The user of the library may not be aware of the fact either,
     because both of these classes may be instantiated in consequence of
     instantiating some third class.

  e) There is no way the user of the library can disambiguate the
     function, so unless (s)he can modify the source code of one of the
     libraries, this will cause serious problems.

So AFAIK this could in some cases produce a call to the wrong
instantiation of the function.

What's more interesting, you can get a similar problems with a single
class in a single translation unit:
(This is similar to the second example I gave in the first article about
this subject)

template <class T>
class X
{
public:
  friend void f() { T x = 4.3; cout << x << endl; }
};

Now instantiate this with X<int> and X<double> and you have two
_different_ definitions for f(). But [temp.friend]/3 says
only the first instantiation defines the function. So does this
mean the order in which the instantiations are made affects the
semantics of the function f() above? (i.e. if X<int> was instantiated
first, then the T in f() is 'int', OTOH if X<double> was
instantiated first, then the T in f() is 'double'?). Or is this use
somehow ill-formed (Where is it stated in the draft?). I couldn't
find anything that disallows the above function.

This problem gets worse, if you add template arguments to a class with a
friend function, and forget to add it into the function signature.
--
   Esa Pulkkinen                        | C++ programmers do it virtually
   E-Mail:  esap@cs.tut.fi              | everywhere with class, resulting
   WWW   :  http://www.cs.tut.fi/~esap/ | in multiple inheritance.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: bparker@gil.com.au (Brian Parker)
Date: 1997/03/11
Raw View
Pulkkinen Esa <esap@cs.tut.fi> wrote:

>Consider the following example:

>template <class T>
>class SomeCategory
>{
>public:
>  // A definition of a friend function depending on the template
>  // argument defined in the friend declaration:
>  inline friend void doSomething(const T& x) { ... }
>};

>template <class T>
>class AnotherCategory
>{
>public:
>  inline friend void doSomething(const T& x) { ... }
>};

>Now assume I'd want to do something like this:

>class MyClass
>: public AnotherCategory<MyClass>,
>  public SomeCategory<MyClass>
>{
>};

>Now, how would I disambiguate the doSomething function so I could decide
>which one to call?

>For example, you can't do this [in MyClass]:

>  friend void doSomething(const MyClass &x)
>  { AnotherCategory<MyClass>::doSomething(x);
>    SomeCategory<MyClass>::doSomething(x);
>  }

>Or is this case simply a violation of the ODR and thus a non-required
>diagnostic, and I have to be careful not to instantiate two classes
>in a way that the instantiations of the friends overlap?

>Also, what happens if the function defined in a friend declaration
>in a class template doesn't depend on the template argument:

>template <class T>
>class X
>{
>  friend void f() { T x; x.print(); }
>};

>Does this make the program invalid (according to the ODR) if the class
>is instantiated more than once? (I.e. can implicit template
>instantiation cause the program be ill-formed according to the ODR?)
>How about if the template has several arguments, and the function only
>depends on some of the arguments? (I'd assume the same answer as the
>previous question). Could the compiler be required to give diagnostic
>on such cases?

I think these are excellent questions. I don't have any answers; just
giving you moral support.
Actually, I found the Dec 96 draft confusing on the issue of friends.
I am not sure if the kind of code used in Barton & Nackman is still
draft-conforming. Sec 14.6.5 [temp.inject] says that friend functions
do not introduce any new names into any scope & then gives an example
of name-dependent lookup looking inside a class to find a friend
definition. I am not clear on how that is supposed to work; I thought
name-dependent lookup started in the namespace surrounding a class,
not in the class itself (at least not for a non-member).

,Brian Parker (bparker@gil.com.au)
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]