1

How do I declare that I want a const function reference (in a template parameter)? e.g.,

template< bool (&func)(int arg) >
void foo(int stuff);

but const?

More concretely, if I try to compile the following with icpc:

template<bool (&func)(int arg)>
bool id(int arg) {
  return func(arg);
}

class Foo {
 public:
  Foo() {};
  virtual ~Foo() {};
  bool bar(int arg) { return true; }
  bool bar2(int arg) {
    return id<bar>(arg);
  };
};

int main() {
  return 0;
}

I get

$ icpc foo.cpp
foo.cpp(12): error: no instance of function template "id" matches the argument list
            argument types are: (int)
      return id<bar>(arg);
             ^
compilation aborted for foo.cpp (code 2)

or, with g++, I get

$ g++ foo.cpp
foo.cpp: In member function ‘bool Foo::bar2(int)’:
foo.cpp:13:23: error: no matching function for call to ‘id(int&)’
     return id<bar>(arg);
                       ^
foo.cpp:13:23: note: candidate is:
foo.cpp:2:6: note: template<bool (& func)(int)> bool id(int)
 bool id(int arg) {
      ^
foo.cpp:2:6: note:   template argument deduction/substitution failed:
foo.cpp:13:23: error: could not convert template argument ‘Foo::bar’ to ‘bool (&)(int)’
     return id<bar>(arg);
                       ^

But if instead I move bar to toplevel, as in

template<bool (&func)(int arg)>
bool id(int arg) {
  return func(arg);
}

bool bar(int arg) { return true; }

class Foo {
 public:
  Foo() {};
  virtual ~Foo() {};
  bool bar2(int arg) {
    return id<bar>(arg);
  };
};

int main() {
  return 0;
}

it compiles fine. Why does this happen, and how can I fix it without making bar a global?

Note: In my original code, I was getting "(not const-qualified) cannot be initialized with a value of type" errors: (with icpc)

CollisionWorld.cpp(73): error: a reference of type "bool (&)(const Line &, vec_dimension={double}, vec_dimension={double}, vec_dimension={double}, vec_dimension={double})" (not const-qualified) cannot be initialized with a value of type "bool (const Line &, vec_dimension={double}, vec_dimension={double}, vec_dimension={double}, vec_dimension={double})"
    QuadTree<Line, vec_dimension, line_inside_box_with_time> *quad_tree =
                                  ^

(with g++)

CollisionWorld.cpp:73:58: error: could not convert template argument ‘CollisionWorld::line_inside_box_with_time’ to ‘bool (&)(const Line&, double, double, double, double)’
   QuadTree<Line, vec_dimension, line_inside_box_with_time> *quad_tree =
                                                          ^
Jason Gross
  • 5,928
  • 1
  • 26
  • 53
  • That has nothing to do with constness. Member functions are not functions! – Kerrek SB Oct 13 '12 at 21:44
  • @KerrekSB member functions are functions. If you name a member function without calling it, it is an rvalue expression of function type. Probably they tried to give a good diagnostic and checked whether the initializer is an rvalue, but forgot about member function initializers. – Johannes Schaub - litb Oct 14 '12 at 19:02
  • @JohannesSchaub-litb: You can call a function, but you can't call a member function. You need an *object* for that. That's something so many people get confused about when they try to have their little callbacks, so I'd like to try out this radical statement in the hope that it gets people to think about what a member function really is... – Kerrek SB Oct 14 '12 at 19:19

2 Answers2

1

The problem is that the template expects a free function, not a member funciton. That is why it works when you put bar() out of Foo,

Try it like this:

template<typename C, bool (C::*func)(int arg)>
bool id(C *mthis, int arg) {
  return (mthis->*func)(arg);
}

class Foo {
 public:
  Foo() {};
  virtual ~Foo() {};
  bool bar(int arg) { return true; }
  bool bar2(int arg) {
    return id<Foo, &Foo::bar>(this, arg);
  };
};
imreal
  • 10,178
  • 2
  • 32
  • 48
  • http://stackoverflow.com/questions/4387971/c-passing-method-pointer-as-template-argument is related. – perh Oct 13 '12 at 21:48
0

To call a member function, you need two things: the this pointer and the function. Therefore it is not possible as easy as you write it. id would need the this pointer!

The template definition would look like this:

template<bool (Foo::*func)(int)>

But still, you cannot implement a true id function that works both on functions and member functions.

Rudolf Mühlbauer
  • 2,511
  • 16
  • 18