Implementation of free functions inside a class valid?

M

Marcel Müller

I found the following code fragment:

template <class T>
class ptr
{private:
T* Ptr;
public:
// ...

friend bool operator==(const ptr<T>& l, const ptr<T>& r)
{ return l.Ptr == r.Ptr; }
friend bool operator==(const ptr<T>& sco, const T* ptr)
{ return sco.Ptr == ptr; }
friend bool operator==(const T* ptr, const ptr<T>& sco)
{ return sco.Ptr == ptr; }
friend bool operator!=(const ptr<T>& l, const ptr<T>& r)
{ return l.Ptr != r.Ptr; }
friend bool operator!=(const ptr<T>& sco, const T* ptr)
{ return sco.Ptr != ptr; }
friend bool operator!=(const T* ptr, const ptr<T>& sco)
{ return sco.Ptr != ptr; }
};

Obviously free functions are implemented within the class body.
Is this valid? And if it is valid are these functions implicitly inline
like other function implementations in the class body?


Marcel
 
A

acehreli

Marcel Müller wrote:
template <class T>
class ptr
{private: [...]
friend bool operator==(const ptr<T>& l, const ptr<T>& r)
{ return l.Ptr == r.Ptr; } [...]
Obviously free functions are implemented within the class body.
Is this valid? And if it is valid are these functions implicitly
inline like other function implementations in the class body?

Uh... The problem here is that those functions should not be
templates (they aren't declared as such), but apparently they
*are* templates since they depend on the 'T'.

I think you misread the code. ptr<T> is the same type as *this in this
case.

I don't have the answer to the original question. I always defined
them outside of the class.

Ali
 
J

Joe Greer

Hm... OK. I'll bite. There is no {*this} in a friend function, is
there? So, what "same type" are we talking about? Friend functions
are stand-alone. They (in most cases) aren't called from any of
member functions. So, if we have the code like

ptr<BLAH> p1, p2;

if (p1 == p2) // hopefully invoking "friend bool operator=="
// defined in the 'prt' class template.

So, we would need the 'friend bool operator' with T == BLAH, right?
Now, if somebody else then writes

ptr<BLEH> pp1, pp2;

if (pp1 == pp2) // ...

then we'd need 'friend bool operator' with T == BLEH, no? That
means that the function that is NOT declared a template _is_ in
fact a template. How did I misread the code?

They are friend functions for class ptr<T>. They can't vary independently
of the class ptr<T>, so I would at least be careful calling them template
functions. I'm trying to decide if there is a downside to this. I know
that with most functions in a template, you only get them if they are used.
Would this also be true for friend functions? One would hope so, but I am
unsure.

joe
 
S

Stefan Ram

Victor Bazarov said:
You start with a wrong premise. There is *no class* ptr<T>. There
is a template 'ptr' with a single template argument. As soon as you
make the argument concrete, you get a class (instantiated class, as
the Standard calls it). So, there can be the _class_ 'ptr<int>' or
the class 'ptr<ptr<ptr<bool> > >'.

»The class collate<charT> provides features for use in the
collation (comparison) and hashing of strings.«

ISO/IEC 14882:2003(E), 22.2.4.1p1

This wording is used in ISO/IEC 14882:2003(E).

»charT« also being the name of a type parameter:

»namespace std {
template <class charT>
class collate : public locale::facet { ...«
 
S

Stefan Ram

You start with a wrong premise. There is *no class* ptr<T>. [...]
»The class collate<charT> provides features for use in the [...]«

This whole notation is based on the mathematical function
notation. If f is a function R->R, then, for each x of R,
f(x) is an element of R, namely, the value of f for x. f(2)
also is an element of R, namely, the value of f for 2.

The sentence »for each x of R, f(x) is an element of R.« does
not contain any free variable anymore. »f(x)« still contains
a free variable x, but in the sentence, x is bound by »for
each x of R«.

In the same manner, on can say »For each type T admissible to
ptr, ptr<T> is a class.«. The »For each type T admissible to
ptr« clause might be implied, even if it is not explicitly
written.
 
G

galathaea

Uh... The problem here is that those functions should not be
templates (they aren't declared as such), but apparently they
*are* templates since they depend on the 'T'. The problem,
therefore, at least IMHO, is how many of them exists in the
wild, and which ones?

This makes me think that such definitions are illegal.

it's the classic barton-nackman trick

it's been around since at least 1994
before even the standard existed
bu it is certainly legal

it's no longer needed
since function templates can be overloaded
but it had it's time

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
galathaea: prankster, fablist, magician, liar
 
G

galathaea

Any _member_ function defined in a template *is itself a template*.
It does not matter if it's a static function or a non-static one.
Member functions of a class template are instantiated just like the
template they're members of.

sorry
but this is not even relevant

this trick generates _nontemplate_ functions
during class template instantiation

fyi

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
galathaea: prankster, fablist, magician, liar
 
K

Kai-Uwe Bux

Victor said:
Uh... The problem here is that those functions should not be
templates (they aren't declared as such), but apparently they
*are* templates since they depend on the 'T'.

Why are they templates and not overloads (any given instantiation of Ptr
will define an overload and befriend that particular overload)?
The problem,
therefore, at least IMHO, is how many of them exists in the
wild, and which ones?

This makes me think that such definitions are illegal.

G++ seems to disagree. If I do

#include <iostream>

template < typename T >
struct X;

template < typename T >
void print ( X<T> const & dummy ) {
std::cout << "external friend\n";
}

template < typename T >
struct X {

friend
void print ( X<T> const & dummy ) {
std::cout << "local friend\n";
}

};

int main ( void ) {
X<int> x;
print( x );
}

I get the output "local friend", and if I do

#include <iostream>

template < typename T >
struct X;

template < typename T >
void print ( X<T> const & dummy ) {
std::cout << "external friend\n";
}

void print ( X<int> const & dummy ) {
std::cout << "non-template external\n";
}

template < typename T >
struct X {

friend
void print ( X<T> const & dummy ) {
std::cout << "local friend\n";
}

};

int main ( void ) {
X<int> x;
print( x );
}

I get a compilation error

redefinition of 'void print(const X<int>&)'

which indicates that the non-template overload is defined twice.

Finally, if I do:

#include <iostream>

template < typename T >
struct X;

template < typename T >
void print ( X<T> const & dummy ) {
std::cout << "external friend\n";
}

void print ( X<int> const & dummy ) {
std::cout << "non-template external\n";
}

template < typename T >
struct X {

friend
void print<> ( X<T> const & dummy );

};

int main ( void ) {
X<int> x;
print( x );
}

I manage to declare the external function a friend. If I leave out the <>, I
don't:

#include <iostream>

template < typename T >
struct X;

template < typename T >
void print ( X<T> const & dummy ) {
std::cout << "external friend\n";
}

void print ( X<int> const & dummy ) {
std::cout << "non-template external\n";
}

template < typename T >
struct X {

friend
void print ( X<T> const & dummy );

};

int main ( void ) {
X<int> x;
print( x );
}

I get a warning, telling me that the friend declaration does not declare a
template function and the output confirms that: "non-template external".

So g++ seems to think that the friend functions defined inside of X are not
templates but ordinary overloads.



Also: the standard might (sic!) disagree. The example in [14.5.3/1] has a
friend function declaration (process) and the text says, it's not a
template:

template<class T> class task;
template<class T> task<T>* preempt(task<T>*);
template<class T> class task {
// ...
friend void next_time();
friend void process(task<T>*);
friend task<T>* preempt<T>(task<T>*);
template<class C> friend int func(C);
friend class task<int>;
template<class P> friend class frd;
// ...
};

I don't see why that should change when the overload is defined within the
class, which seems to be permitted.


Best

Kai-Uwe Bux
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top