Topic: Template classes with member function args


Author: daniels@NeoSoft.com (Brad Daniels)
Date: Wed, 14 Apr 1993 19:59:02 GMT
Raw View
Since this thread discusses standards issues, I've cross-posted to
comp.std.c++.  Followups still go to comp.lang.c++, though, since
this is all probably as much or more an issue of misunderstandings
as of grey areas in the language definition.

In article <HARALD.93Apr14171101@marilyn.bion.kth.se> harald@marilyn.bion.kth.se
 (Harald Winroth) writes:
>However, I can't find anything in ARM that suggests that the code is illegal.
>Does anyone knows if this is supposed to work?
>
>#include <stream.h>
>
>template<void (*f)()> class Foo
>{
>public:
>    Foo() { f(); }
>};
>
>class Bar
>{
>public:
>    static void g() { cout << "g()" << endl; }
>    Foo<g> foo;
>};
>
>int main (int argc, char *argv[])
>{
>    Bar bar;
>    return 0;
>}

Not having the ARM handy I can't say definitively whether the above is
legal, but it's definitely not consistent with the way I usually think
of template classes.  A template class is usually a parametrized
type, meaning roughly that it is a type which may contain elements of other
types, with those other types being specified at a later time.  You seem to
be using it as some kind of extended macro facility.  In any case, the program
gives me an error from DEC C++ to the effect that "Bar::g" does not have
external linkage, so its address may not be used as a template argument.
While the reasons behind this restriction are a bit confusing to me, the
remedy is at least clear: make the function g() have external linkage.
Hardly the nicest solution, but...  I tried saying explicitly Foo<Bar::g>,
but that was not kosher as far as DEC C++ is concerned.

What you're trying to do doesn't require templates, though, and the use of
templates is only likely to lead to confusion (well, it confused me,
anyway... :-)  I would be inclined to write the program as:

#include <iostream.h>

class Foo
{
public:
    Foo(void (*f)()) { f(); }
};

class Bar
{
public:
    static void g() { cout << "g()" << endl; }
    Foo foo;
    Bar() : foo(g) {};
};

int main (int argc, char *argv[])
{
    Bar bar;
    return 0;
}

Granted, this means that you have to have a separate initializer foo(g)
for every ctor in any class with instances of foo, but it feels a little
clearer to me, and more importantly, it works where the other doesn't.

Also, I noticed that the original program says "template<void (*f)()> class
foo", but "... class Foo<f>" works also.  Since both work, I take it that the
<f> is optional.  Is including it an anchronism?  I like the look of including
it, though you can get a nasty case like:

#include <stream.h>

template<class T1, class T2> class Foo<T2,T1> {
    T1 v1;
    T2 v2;
public:
    Foo() { cout << "v1 is type " << v1 << "\nv2 is type " << v2 <<"\n"; }
};

class typ1 {
public:
    friend ostream &operator<<(ostream &out,typ1 v) { return out<<"typ1"; }
};

class typ2 {
public:
    friend ostream &operator<<(ostream &out,typ2 v) { return out<<"typ2"; }
};

main() {
    Foo<typ1,typ2> tmp;
    return 0;
}

Notice that the order of T1 and T2 is reversed in Foo<T2,T1> over that
inside the template<> pointy braces.  On DEC C++, the order specified
in the template<> pointy braces predominates.  Is this an undefined situation,
or am I missing something?

- Brad
--
Brad Daniels  ` |  "If money can't buy happiness,
daniels@neosoft.com  |   I guess I'll have to rent it."
I don't work for NeoSoft, and |  - Weird Al Yenkovic
don't speak for my employer. |