function pointer as template parameter + type deduction

Discussion in 'C++' started by er, Jun 7, 2009.

  1. er

    er Guest

    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)?
     
    er, Jun 7, 2009
    #1
    1. Advertising

  2. er

    er Guest

    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.
     
    er, Jun 7, 2009
    #2
    1. Advertising

  3. * 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

    --
    Due to hosting requirements I need visits to <url: http://alfps.izfree.com/>.
    No ads, and there is some C++ stuff! :) Just going there is good. Linking
    to it is even better! Thanks in advance!
     
    Alf P. Steinbach, Jun 7, 2009
    #3
  4. er

    er Guest

    On Jun 7, 4:34 am, "Alf P. Steinbach" <> wrote:
    > * 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
    >
    > --
    > Due to hosting requirements I need visits to <url:http://alfps.izfree.com/>.
    > No ads, and there is some C++ stuff! :) Just going there is good. Linking
    > to it is even better! Thanks in advance!


    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.
     
    er, Jun 7, 2009
    #4
  5. er wrote:

    > 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
    --
    a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
    c.l.c FAQ: http://c-faq.com/
    c.l.c++ FAQ: http://www.parashift.com/c -faq-lite/
     
    Bart van Ingen Schenau, Jun 8, 2009
    #5
  6. er

    er Guest

    On Jun 8, 2:01 pm, Bart van Ingen Schenau <>
    wrote:
    > er wrote:
    > > 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.


    Thanks. I was hoping some non-trivial named parameters (as opposed to
    positional) approach or a related pattern could break free of this
    rule.
     
    er, Jun 9, 2009
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Neelesh

    Template parameter Deduction

    Neelesh, Nov 10, 2005, in forum: C++
    Replies:
    4
    Views:
    436
    John Carson
    Nov 10, 2005
  2. Fei Liu
    Replies:
    0
    Views:
    425
    Fei Liu
    Oct 25, 2007
  3. Fei Liu
    Replies:
    4
    Views:
    785
    Victor Bazarov
    Oct 26, 2007
  4. C++Liliput
    Replies:
    2
    Views:
    788
    James Kanze
    Oct 24, 2008
  5. Replies:
    2
    Views:
    326
Loading...

Share This Page