1

I am trying to pass function as template argument to a function in a class, but there is some mistake. The code is giving an error error: missing template arguments before 'obj'. How can I fix this so that it compiles?

#include<iostream>
double sum_fun(const double &a, const double &b)
{ std::cout<<a+b<<"\n"; }

template <typename F>
class B 
{
    public:
    void fb()(F f1)
    {
        f1(10.1,10.2);
    }
};

int main()
{
    B obj(sum_fun); //error
    obj.fb(); //error
    return 0;
}
TrebledJ
  • 8,713
  • 7
  • 26
  • 48
ewr3243
  • 397
  • 3
  • 19

2 Answers2

3

There is a misunderstanding of how classes work.

int main()
{
    B obj(sum_fun); // calls B constructor with parameter `sum_fun`
    obj.fb(); // calls member function B::fb() with no parameters
    return 0;
}

Both lines raise an error as

  1. Your class has no constructor which takes a single parameter.
  2. void fb()(F f1) is illegal syntax. To declare a member function, use only one set of parentheses: either void fb() or void fb(F f1). The latter is incorrect in our case, as your member function call obj.fb() passes no parameters.

To fix this, write up a constructor, store the function as a member variable, and use that variable in the function fb().

template <typename F>
class B 
{
public:
    // constructor, initialises member `m_func` through member initialisation
    B(F func) : m_func(func) {}

    void fb()
    {
        m_func(10.1,10.2);
    }

private:
    F m_func;

};

In C++17, thanks to automatic template deduction, no errors are now emitted. But in lower standards (e.g. C++11), template deduction is lacking and thus, the full templated type needs to be specified when declaring obj.

So in standards below C++17, the main function should be:

int main()
{
    // C++11: using a function pointer to denote type
    B<double(*)(const double&, const double&)> obj(sum_fun);

    // ok in C++17, looks cleaner too
    // B obj(sum_fun);

    obj.fb();
    return 0;
}

Here, double(*)(const double&, const double&) is a function pointer, i.e. a pointer to a function which returns a double and takes two parameters, both of type const double&. Function pointers may be considered as a type, which satisfies the template (template<typename F>).

Just like we do std::vector<int> and std::vector<double>, we can also do std::vector<double(*)(const double&, const double&)> to denote a vector of functions returning double and taking const double& as parameters.

And by the way, sum_fun also raises a warning: nothing is returned even though the return type is double... better specify void as the return type instead.

C++11 Demo
C++17 Demo


Is it possible to pass function as argument directly to B::fb() instead of creating constructor B::B(F) and storing in local variable?

Certainly.

#include <iostream>

void sum_fun(const double& a, const double& b)
{
    std::cout << a+b << "\n";
}

template <typename F>
class B 
{
public:
    void fb(F func)
    {
        func(10.1,10.2);
    }
};

int main()
{
    B<void(*)(const double&, const double&)> obj;
    obj.fb(sum_fun);
    return 0;
}

Note that the member function fb now takes a single parameter func, which we then call. Note also that in C++17, we now can't instantiate the obj with B obj; because this would be ambiguous and the template can't be deduced automatically. Instead, we need to specify the full type B<void(*)(const double&, const double&)>.

However, a recommended alternative over function pointers is to use std::function, which is more versatile and offers a more readable syntax. (std::function Demo)

TrebledJ
  • 8,713
  • 7
  • 26
  • 48
  • The other problem is that he needs to specify `F` when he creates the object. – David G Aug 01 '19 at 00:49
  • @0x499602D2 op does use `F` (in `void fb()(F f1)`) but the syntax is wrong... I'm probably misunderstanding your comment. Some clarification would help. :| – TrebledJ Aug 01 '19 at 00:58
  • He does `B obj` when it should be `B obj` – David G Aug 01 '19 at 01:04
  • @0x499602D2 Ah, I see. I was compiling with C++17 (which doesn't complain about `B obj`). Thanks for the heads up. – TrebledJ Aug 01 '19 at 01:17
  • @TrebledJ can you comment on 2 things : (1) Is it possible to pass function as argument directly to `B::fb()` instead of creating constructor `B::B(F)` and storing in local variable? (2) Can you explain what is `B` and how it works? I am particularly confused with `double(*)` Thanks ! – ewr3243 Aug 01 '19 at 16:29
  • @ewr3243 I've edited and expanded my answer to answer your questions. In short, (1) Yes, it is possible; (2) It's a function pointer... something I don't often see used. I usually use std::function instead. Hope the edit helps. – TrebledJ Aug 02 '19 at 11:40
0

In C++17 you're allowed to use auto in template paramter list:

template <auto F>
class B
{
    public:
    void fb()
    {
        F(10.1,10.2);
    }
};

You can then do B<sum_fun>:

int main()
{
    B<sum_fun> obj{};
    obj.fb();
    return 0;
}
Andreas DM
  • 10,685
  • 6
  • 35
  • 62