function pointer as template parameter + type deduction

E

er

Hi,

here's a problem:

struct A{};

void f(const A&a){}

template<typename T,void (*)(const T&)>
void g(const T& t){}

template<typename T>
void g2(const T& t,void (*)(const T&)){}

A a;

g<f>(a); // (1)
g<A,f>(a); // (2)
g2(a,f); // (3)

(2) and (3) compile fine, not 1. Why exactly? Any suggestion to
approach (1)?
 
E

er

PS: g++ --version
i686-apple-darwin9-g++-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5490)
Copyright (C) 2005 Free Software Foundation, Inc.
 
A

Alf P. Steinbach

* er:
Hi,

here's a problem:

struct A{};

void f(const A&a){}

template<typename T,void (*)(const T&)>
void g(const T& t){}

What's the point of the unnamed template parameter?

One suspects a case Evil Premature Optimization, that the intention is to shave
a (conjectured but quite possibly not even real) nano-second by calling some
routine "directly" instead of having it passed as a routine pointer argument.

EPO is unfortunately a root cause of so much extreme and unnecessary complexity.

template<typename T>
void g2(const T& t,void (*)(const T&)){}

A a;

g<f>(a); // (1)
g<A,f>(a); // (2)
g2(a,f); // (3)

(2) and (3) compile fine, not 1. Why exactly?

You have defined g with two template parameters, one which is anonymous and
therefore cannot ever be deduced, hence must always be explicitly specified.

Any suggestion to approach (1)?

Don't. :)

You're into the second root cause of extreme and unnecessary complexity, namely
the Elegant Notation Fetish (ENF), where almost any absurdly huge baggage of
cryptic, complex, counter-intuitive code is deemed acceptable to shave /one/
character, or perhaps two, in a single very unimportant expression somewhere.

But if you strongly feel that providing the type parameter is very un-elegant,
that it simply must be deduced from the specified function, then perhaps like

<code>
struct A{};
void f(const A&a){}

struct F
{
typedef A ArgType;
static void effect( ArgType const& a ) { ::f( a ); }
};

template< class Func >
void g( typename Func::ArgType const& t ) {}

int main()
{
A a;
g<F>(a);
}
</code>


Cheers & hth.,

- Alf
 
E

er

* er:






What's the point of the unnamed template parameter?

One suspects a case Evil Premature Optimization, that the intention is to shave
a (conjectured but quite possibly not even real) nano-second by calling some
routine "directly" instead of having it passed as a routine pointer argument.

EPO is unfortunately a root cause of so much extreme and unnecessary complexity.





You have defined g with two template parameters, one which is anonymous and
therefore cannot ever be deduced, hence must always be explicitly specified.


Don't. :)

You're into the second root cause of extreme and unnecessary complexity, namely
the Elegant Notation Fetish (ENF), where almost any absurdly huge baggage of
cryptic, complex, counter-intuitive code is deemed acceptable to shave /one/
character, or perhaps two, in a single very unimportant expression somewhere.

But if you strongly feel that providing the type parameter is very un-elegant,
that it simply must be deduced from the specified function, then perhaps like

<code>
struct A{};
void f(const A&a){}

struct F
{
     typedef A ArgType;
     static void effect( ArgType const& a ) { ::f( a ); }

};

template< class Func >
void g( typename Func::ArgType const& t ) {}

int main()
{
     A a;
     g<F>(a);}

</code>

Cheers & hth.,

- Alf

Thanks. Your answer is not lost on me although it answers a different
question from the one I (vaguely) intended. Here's a corrected and
extended version:

struct A{};
struct B{};
void f1(const A&a){}
void f1(const B&b){}
void f2(const A&a){}
void f2(const B&b){}

template<typename T,void (*fun)(const T&)>
void g(const T& t){}

template<typename T>
void g2(const T& t,void (*fun)(const T&)){}

template<typename T,void (*fun)(const T&)>
struct fun_ptr{
static void call(const T& t){ return fun(t); }
};

template<typename T>
struct f1_ : fun_ptr<T,f1>{};
template<typename T>
struct f2_ : fun_ptr<T,f2>{};

template<template<typename> class F,typename T>
void g3(const T& t){
typedef F<T> fun_t;
return fun_t::call(t);
}

//g<f1>(a); //won't compile
g<A,f1>(a);
g2(a,f1);
g3<f1_>(a);


g3 does the job i.e. 1) function pointer passed as a template argument
2) T is deduced from arguments, but it requires an f_ for each f.
 
B

Bart van Ingen Schenau

er said:
Thanks. Your answer is not lost on me although it answers a different
question from the one I (vaguely) intended. Here's a corrected and
extended version:

struct A{};
struct B{};
void f1(const A&a){}
void f1(const B&b){}
void f2(const A&a){}
void f2(const B&b){}

template<typename T,void (*fun)(const T&)>
void g(const T& t){}

template<typename T>
void g2(const T& t,void (*fun)(const T&)){}

template<typename T,void (*fun)(const T&)>
struct fun_ptr{
static void call(const T& t){ return fun(t); }
};

template<typename T>
struct f1_ : fun_ptr<T,f1>{};
template<typename T>
struct f2_ : fun_ptr<T,f2>{};

template<template<typename> class F,typename T>
void g3(const T& t){
typedef F<T> fun_t;
return fun_t::call(t);
}

//g<f1>(a); //won't compile
g<A,f1>(a);
g2(a,f1);
g3<f1_>(a);


g3 does the job i.e. 1) function pointer passed as a template argument
2) T is deduced from arguments, but it requires an f_ for each f.

There is a small, but very significant difference in the template
parameters of g() and g3(). Perhaps it becomes obvious if we put the two
declarations next to each other:
template<typename T,void (*fun)(const T&)> void g (const T& t);
template<template<typename> class F,typename T> void g3(const T& t);

As you can see, in g() the template parameter T comes first, but in
g3(), T comes last.
The problem is that any template arguments that you specify explicitly
are bound to the first N template parameters and only the remaining
(unspecified) parameters are used in the template argument deduction
based on the function-call arguments.
In the call 'g<f1>(a)', the specified argument 'f1' is bound to the
first template parameter (T) which fails because a type is expected
instead of a value.

Unfortunately, you are in a double bind here, because there is no way to
specify the template parameters of g() such that the call 'g<f1>(a)'
becomes well-formed.
Your best choice is to use the pattern of g2().

Bart v Ingen Schenau
 
E

er

There is a small, but very significant difference in the template
parameters of g() and g3(). Perhaps it becomes obvious if we put the two
declarations next to each other:
 template<typename T,void (*fun)(const T&)>      void g (const T& t);
 template<template<typename> class F,typename T> void g3(const T& t);

As you can see, in g() the template parameter T comes first, but in
g3(), T comes last.
The problem is that any template arguments that you specify explicitly
are bound to the first N template parameters and only the remaining
(unspecified) parameters are used in the template argument deduction
based on the function-call arguments.

Thanks. I was hoping some non-trivial named parameters (as opposed to
positional) approach or a related pattern could break free of this
rule.
 

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,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top