c++vectortransformpointer-to-member

Calling member function in transform()


So I've been trying to use the transform function to create a vector from 2 existing ones. I succesfully managed to do it without classes but now I want to use classes and I get errors. The code bellow is only a small, relevant part of my project so it might seem silly but the whole thing actually makes sense in the end.

Here is part of my header file :

#include <vector>
#include <algorithm>

class Example
{
public:
    double multiply(double x, double y);
    void generate_amplitude(std::vector<double>& ampVec);
};

And some content of my .cpp file :

#include "example.h"

double Example::multiply(double x, double y)
{
    return x*y;
}

void Example::generate_amplitude(std::vector<double>& ampVec)
{
    double const a = 3.14;
    int const lenVectors = 10;

    std::vector<double> timeVec = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9}
    std::vector<double> ampVec(lenVectors);
    std::vector<double> aVec(lenVectors, a);

    // Fill ampVec with timeVec[i]*aVec[i] for i in range[0:lenVectors[
    transform(timeVec.begin(), timeVec.end(), aVec.begin(), ampVec.begin(), multipy);
}

So when I try to compile, I get :

error: must use '.*' or '->*' to call pointer-to-member function in '__binary_op (...)', e.g. '(... ->* __binary_op) (...)'

I did search and read for hours about pointer-to-member functions, tried a few different ways like :

double (Example::*pmf)(double, double);
pmf = &Example::multiply;
transform(timeVec.begin(), timeVec.end(), aVec.begin(), ampVec.begin(), this->*pmf);

but I'm still new to this and couldn't find a way to make it work. This particular example returned that kind of message :

error: no matching function for call to 'transform(std::vector<double>::iterator, std::vector<double>::iterator, std::vector<double>::iterator, std::vector<double>::iterator, double (Example::)(double, double))'
note: candidate: 'template<class _IIter, class _OIter, class _UnaryOperation> _OIter std::transform(_IIter, _IIter, _OIter, _UnaryOperation)'
note:   candidate expects 4 arguments, 5 provided
note:   template argument deduction/substitution failed
note: candidate: 'template<class _IIter1, class _IIter2, class _OIter, class _BinaryOperation> _OIter std::transform(_IIter1, _IIter1, _IIter2, _OIter, _BinaryOperation)'
note:   member function type 'double (Example::)(double, double)' is not a valid template argument

Help would be much appreciated !


Solution

  • The simplest way to approach this problem would be with the use of lambdas:

    transform(
        timeVec.begin(), timeVec.end(),
        aVec.begin(),
        ampVec.begin(),
        [this](double a, double b) { return multiply(a, b); }
    );
    

    You can't use a pointer-to-member-function here because you need the this pointer upon which to invoke the pointer-to-member-function, and std::transform() has no mechanism to supply this information. Capturing this with the lambda solves that problem by making this part of the state of the functor.

    You could do something similar in C++03 by writing a custom functor:

    class ExampleMultiply {
    public:
        explicit ExampleMultiply(Example &p) : example(p) {}
    
        double operator()(double a, double b) const {
            return example.multiply(a, b);
        }
    
    private:
        Example &example;
    };
    

    Then invoking std::transform() with this functor:

    transform(
        timeVec.begin(), timeVec.end(),
        aVec.begin(),
        ampVec.begin(),
        ExampleMultiply(*this)
    );
    

    Under the hood, this is basically what the compiler generates to implement the lambda expression.