Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Acquire a function type for a member of a class template?

Tags:

c++

templates

I am trying to write a float(float) function class

#pragma once
class float_func
{
    struct concept 
    {
        virtual ~concept() {}
        virtual float operator()(float) = 0;
        virtual concept* clone() = 0;
    };
    template <typename T>
        class impl : public concept
    {
        public:
            impl(const T& ptr) : ptr_(ptr) {};
            virtual float operator()(float arg1) {
                return ptr_(arg1);
            }
            virtual concept* clone() {
                return new impl<T>(ptr_);
            }
        private:
            T ptr_;

    };

    public:
    float_func() { object_ = nullptr; }
    template <typename T> float_func(const T& ptr) : object_(new impl<T>(ptr)) {}
    float_func(const float_func& other) : object_(other.object_->clone()) {}
    ~float_func() { delete object_; }
    template <typename T> 
    float_func& operator=(const T& ptr) {
        delete object_;
        object_ = new impl<T>(ptr);
        return *this;
    }
    float_func& operator=(const float_func& other) {
        delete object_;
        object_ = other.object_->clone();
        return *this;
    }
    float operator()(float arg1) {
        return (*object_)(arg1);
    }
    private:
    concept* object_;
};

Try to use it:

    #include <iostream>
    #include <functional>
    #include "float_func.h"

    struct FloatFunctor
    {
        float operator()(float a) {
            return a * 2.f;
        }
    };

    float mul3(float a) {
        return a * 3.f;
    }

    int main()
    {
        float_func f1 = FloatFunctor();
        float_func f2 = &mul3;
        //float_func f3 = mul3; //! does not compile
        std::function<float(float)> sf = mul3;
        std::function<float(float)> sf2 = &mul3;
        std::cout << f1(1) << " " <<  f2(2) <<  " " << sf(3) << " " << sf2(4) << std::endl;
        return 0;
    };

float_func f3 = mul3 does not compile, but the stl version does compile.

The error message:

float_func.cc
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\INCLUDE\xlocale(336) : wa
rning C4530: C++ exception handler used, but unwind semantics are not enabled. S
pecify /EHsc
e:\src\tmp\type_erasure\float_func.h(14) : warning C4180: qualifier applied to f
unction type has no meaning; ignored
        e:\src\tmp\type_erasure\float_func.h(28) : see reference to class templa
te instantiation 'float_func::impl<T>' being compiled
        with
        [
            T=float (float)
        ]
        float_func.cc(20) : see reference to function template instantiation 'fl
oat_func::float_func<float(float)>(T (__cdecl &))' being compiled
        with
        [
            T=float (float)
        ]
e:\src\tmp\type_erasure\float_func.h(22) : error C2207: 'float_func::impl<T>::pt
r_' : a member of a class template cannot acquire a function type

Could you explain the error?

How does std::function handle this kind of template argument or how could I modify my code to support float_func f3 = mul3?

like image 546
Jichao Avatar asked Mar 22 '23 16:03

Jichao


1 Answers

The expression mul3 has a function type: float(float) and not a function pointer type (like float (*)(float)). Therefore, the ctor template of float_func deduces T to be this function type float(float):

template <typename T> float_func(const T& ptr)

After substitution:

float_func(float (&ptr)(float)) // T = float(float)

This leads to the instantiation of impl<float(float)> which in turn tries to declare a member of this function type (T ptr_) -- but this is illegal [temp.arg.type]/3:

If a declaration acquires a function type through a type dependent on a template-parameter and this causes a declaration that does not use the syntactic form of a function declarator to have function type, the program is ill-formed. [Example:

template<class T> struct A {
    static T t;
};
typedef int function();
A<function> a; // ill-formed: would declare A<function>::t
               // as a static member function

end example ]


The simplest solution I can think of is to take the sink argument by value:

template <typename T> float_func(T ptr)

This deduces a function pointer type for expressions of function type (forcing a function-to-pointer conversion).

Another way is to instantiate impl with a decayed T, e.g. new impl< typename std::decay<T>::type >(ptr)

like image 63
dyp Avatar answered Apr 01 '23 22:04

dyp