Francis Cugler
Francis Cugler

Reputation: 7925

How to properly invoke a function object within a class template's member function? Visual Studio Compiler Error C2440 is being generated

This is a follow up to this question found here!

Now, that I'm able to instantiate the object. I'm now getting a Visual Studio C2440 Compiler Error...


In my original code before it was templated I had a set of member functions that worked on the std::function<double(double)> member object that looked like this:

struct SomeStruct {
    double a_;
    double b_;
    SomeStruct(double a, double b) : a_{a}, b_{b} {}
};

class SomeClass {
private:
    SomeStruct fields_;
    size_t n_;
    std::function<double(double)> func_;

public:
    SomeClass(SomeStruct fields, size_t n, std::function<double(double)> func) :
      fields_{fields}, n_{n}, func_{func}
    {}

    double evaluate() {
        auto distance = fields_.a_ - fields_.b_;  
        auto dx = distance / n_;
        return calculate(dx, fields_);
    }

private:
    double calculate(double dx, const SomeStruct& someStruct) {
        double result = 0;
        for (size_t i = 0; i < n_; ++i) {
            auto dy = func_(someStruct.a_ + i * dx);
            auto area = dy * dx;
            result += area;
        }
        return result;
    }
};

Now my class looks like this:

template<typename Field>
struct SomeStruct {
    Field a_;
    Field b_;
    constexpr SomeStruct(Field a, Field b) : a_{a}, b_{b} {}
};

template<typename FieldType, typename ValueType, typename Func>
class SomeClass {
private:
    SomeStruct<FieldType> fields_;
    size_t n_;
    Func func_; 

public:
    SomeClass(SomeStruct<FieldType> fields, size_t n, Func func) :
        fields_{fields}, n_{n}, func_{func}
    {}

    constexpr ValueType evaluate() {
        auto distance = fields_.a_ - fields_.b_;       
        auto dx = distance / n_;              
        return calculate(dx, fields_);
    }

private:
    constexpr ValueType calculate(ValueType dx, const SomeStruct<FieldType>& someStruct) {
        ValueType result = 0;
        for (size_t i = 0; i < n_; ++i) {
            auto dy = func_(someStruct.a_ + i * dx);
            auto area = dy * dx;
            result += area;
        }
        return result;
    }
};

template<typename FieldType, typename ValueType, typename Func>
SomeClass(SomeStruct<FieldType>, ValueType, Func) ->
SomeClass<FieldType, ValueType, Func>;

I'm now trying to use the class as such:

template<typename T>
constexpr T funcA(T x) {
    return x;
}

template<typename T>
constexpr T funcB(T x) {
    return x*x;
}

int main() {
    SomeClass a{SomeStruct{1.0, 3.0}, 1000, &funcA<double>};
    // a.evaluate();

    SomeClass b{SomeStruct{3.5, 7.5}, 2000, &funcB<double>};
    // b.evaluate();
    return 0;
}

And I'm getting this Visual Studio error...

1>------ Build started: Project: Computations, Configuration: Debug Win32 ------
1>main.cpp
1>c:\users\...\main.cpp(33): error C2440: 'initializing': cannot convert from 'initializer list' to 'Integrator<double,int,T (__cdecl *)(T)>'
1>        with
1>        [
1>            T=double
1>        ]
1>c:\users\...\main.cpp(33): note: No constructor could take the source type, or constructor overload resolution was ambiguous
1>Done building project "Computations.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

This is being generated before I even call its public evaluate() function that will call its private calculate() function...

I'm trying to resolve this compiler error, and I'm trying to make sure that I'm able to invoke the function object, function pointer, functor, lambda that is stored within this class properly...

Upvotes: 0

Views: 118

Answers (1)

Piotr Skotnicki
Piotr Skotnicki

Reputation: 48527

The problem is in the declaration of data member Func:

Func* func_; 

It declares func_ to be of a pointer to the type deduced by a deduction guide:

template <typename FieldType, typename ValueType, typename Func>
SomeClass(SomeStruct<FieldType>, ValueType, Func)
    -> SomeClass<FieldType, ValueType, Func>;

By calling the constructor with an address of a function:

SomeClass a{SomeStruct{1.0, 3.0}, 1000, &funcA<double>};

Func is deduced as:

Func = double(*)(double)

Thus, Func* becomes a pointer to a function pointer:

Func* = double(**)(double)

and that cannot be initialized with &funcA<double>. Instead, use Func alone as the data member type.


I'm trying to make sure that I'm able to invoke the function object, function pointer, functor, lambda that is stored within this class properly

Either use a concept for that or a static assert:

static_assert(std::is_invocable_v<Func&, FieldType&>, "Invalid callable");

Upvotes: 0

Related Questions