c++templatesinheritancestd-functionstdbind

How to use a base class template member function in derived?


how to use a base class template function in a derived case?

Example:

Class A{

    protected:
       template<typename... Args>
            std::vector<torch::Tensor> generate_value(const size_t& generated_points, std::function<int(const Args&...)> function, const Args&... args);
};
class B : protected A{
   public:
      B() : A(){}
   protected:
     std::vector<torch::Tensor> generate_values(const size_t& generated_points, const size_t& column){
        return this->generate_values(generated_points, std::bind(&A::inner_generation, this, std::placeholders::_1), column);
     }

     <torch::Tensor inner_generation(const size_t& column);
};

Compiling use Clang I get the following error:

 error: no matching member function for call to 'generate_values'
        return this->generate_values(generated_points, function, column);
               ~~~~~~^~~~~~~~~~~~~~~
note: candidate template ignored: could not match 'std::function<torch::Tensor (const Args &...)>
            std::vector<torch::Tensor> generate_values(const size_t& generated_points, std::function<torch::Tensor(const Args&...)> function, const Args&... args)

I am not sure what is the issue.

I hope someone could guide me.

Originally, I had the function with same name, it was only finding the one in class B. I have then changed the names it is still not found. I am not sure is that due to the template.


Solution

  • std::function is rather expensive in usage and little flexible – if you go along with yet another template parameter you are more efficient, more flexible and even get simpler code:

    class A
    {
    protected:
        template<typename F, typename... Args>
        std::vector<torch::Tensor> generate_value
        (
            size_t generated_points, // no benefit from reference...
            F&& f // the function/functor to execute
            Args&&... args // allows perfect forwarding
        )
        {
            f(std::forward<Args>(args)...);
        } 
    };
    
    std::vector<torch::Tensor> B::generate_values(size_t generated_points, size_t column)
    {
        return generate_values
        (
            generated_points,
            // simply use a lambda, it's just more convenient:
            [this](size_t column) { return inner_generation(column); },
            // though the result of `bind` would be just as valid...
            column // and the parameter...
        );
    }
    

    Though if using lambdas (or std::bind as well, if you bind the values directly instead of using place holders) you don't even need additional parameters:

    template<typename F>
    std::vector<torch::Tensor> A::generate_value(size_t generated_points, F&& f)
    {
        f();
    }
    
    std::vector<torch::Tensor> B::generate_values(size_t generated_points, size_t column)
    {
        return generate_values
        (
            generated_points,
            [this, column] { return inner_generation(column); }
            // alternatively with default bindings:
            //[=] { return inner_generation(column); }
        );
    }
    

    Which one to choose? Well, a matter of personal taste mainly – unless you want to do the same multiple times with different variables, then the former variant is of advantage for not having to create multiple lambdas:

    std::vector<torch::Tensor> B::generate_values
    (
        size_t generated_points,
        size_t column1,
        size_t column2 // now one more...
    )
    {
        auto gen = [this](size_t column) { return inner_generation(column); };
        auto v1 = generate_values(generated_points, gen, column1);
        auto v2 = generate_values(generated_points, gen, column2);
        // union both, create difference or do whatever is appropriate...
        return ...;
    }
    

    This latter example might not be meaningful in your concrete case, but might come into play in other scenarios ;)

    Side note: Just for explanation: The original variant fails for neither the result of bind nor a lambda actually being a std::function object, thus not matching your template parameter. You can solve by creating the std::function object explicitly:

    return A::generate_values
    (
        generated_points,
        std::function([this](size_t const& column) { return inner_generation(column); }),
        // alternatively with `bind`ing, though the place holder makes
        // explicitly specifying the template argument necessary:
        //std::function<torch::Tensor(size_t const&)>
        //(
        //    std::bind(&B::inner_generation, this, std::placeholders::_1)
        //),
        column
    );
    

    Note, too, that in the original code of the question the return types of the std::function instantiations do not match either (first int, then torch::Tensor – unless torch::Tensor happens to be a typedef for int…).