Home > other >  specialize operator() with enums for different compile time results
specialize operator() with enums for different compile time results

Time:04-22

I would like to know if it is possible to specialize the operator(): I would like to be able to call different variants of operator() at compile time. The code bellow provides an example of what is expected.

#include <vector>
#include <iostream>
#include <string>
#include <numeric>
#include <array>

enum OpSelector {stdOp, Op1, Op2};
typedef std::array<double, 3> vec;

template<class T, OpSelector S = stdOp>
class classA
{
public:

    classA() {}

    double operator()(const double& a, const T& b) const
    {
        return (a & b);
    }
};

// Default function
template<class T>
double operator&( const double& a, const T& b )
{
    std::cout << "Operation 0" << std::endl;
    return 0;
}

// Specialized function
template<>
double classA<vec, Op1>::operator()(const double& a, const vec& b) const
{
    std::cout << "Operation 1" << std::endl;
    return 1;
}

// Specialized function
template<>
double classA<vec, Op2>::operator()(const double& a, const vec& b) const
{
    std::cout << "Operation 2" << std::endl;
    return 2;
}

int main(int argc, char** argv)
{
    classA<vec> obj;
    vec v {1,2,3};
    obj(1.2, v); // works
    obj<Op1>(1.2, v); // Does not work

   return 0;
}

Is it possible to achieve this through specialization/ some other means?

CodePudding user response:

To make your example work, you have to move OpSelector template argument from the class template to the operator() function (which needs to be templated):

#include <vector>
#include <iostream>
#include <string>
#include <numeric>
#include <array>

enum OpSelector {stdOp, Op1, Op2};
typedef std::array<double, 3> vec;

template<class T>
class classA
{
public:
    classA() {}

    template <OpSelector S = stdOp>
    double operator()(const double& a, const T& b) const;
};

// Specialized function
template<>
template<>
double classA<vec>::operator()<stdOp>(const double& a, const vec& b) const
{
    std::cout << "Operation 0" << std::endl;
    return 1;
}

template<>
template<>
double classA<vec>::operator()<Op1>(const double& a, const vec& b) const
{
    std::cout << "Operation 1" << std::endl;
    return 1;
}

// Specialized function
template<>
template<>
double classA<vec>::operator()<Op2>(const double& a, const vec& b) const
{
    std::cout << "Operation 2" << std::endl;
    return 2;
}

int main(int argc, char** argv)
{
    classA<vec> obj;
    vec v {1,2,3};
    obj(1.2, v); // works
    obj.template operator()<Op1>(1.2, v);

    // prints
    // Operation 0
    // Operation 1

   return 0;
}

The downside to this solution is how the specialized operator() function has to be called (.template operator()<Op1>).

(You can play with this here: https://godbolt.org/z/rvGTWhjx1)

If you decide to use a named function instead of operator(), the calling syntax is closer to what you seem to have intended:

template<class T>
class classA
{
public:
    classA() {}
    template <OpSelector S = stdOp>
    double func(const double& a, const T& b) const;
};

template<>
template<>
double classA<vec>::func<stdOp>(const double& a, const vec& b) const
{
    std::cout << "Operation 0" << std::endl;
    return 1;
}

/* ... further specializations ... */

void test()
{
    classA<vec> obj;
    vec v {1,2,3};
    obj.func(1.2, v);
    obj.func<Op1>(1.2, v);
}

CodePudding user response:

S is a template parameter of the class not the operator so wont do what you want. Even if you do make the operator templated the syntax to call it would be really awkward: obj.template operator()<Op1>(1.2, v);, you'd be better of making a named function instead which you can then call how you want to, e.g: bj.foo<Op1>(1.2, v);:

#include <vector>
#include <iostream>
#include <string>
#include <numeric>
#include <array>

enum OpSelector {stdOp, Op1, Op2};
typedef std::array<double, 3> vec;

template<class T>
class classA
{
public:

    classA() {}

    template<OpSelector S = stdOp>
    double foo(const double& a, const T& b) const;
};

// Default function
template<>
template<>
double classA<vec>::foo<stdOp>( const double& a, const vec& b ) const
{
    std::cout << "Operation 0" << std::endl;
    return 0;
}

// Specialized function
template<>
template<>
double classA<vec>::foo<Op1>(const double& a, const vec& b) const
{
    std::cout << "Operation 1" << std::endl;
    return 1;
}

// Specialized function
template<>
template<>
double classA<vec>::foo<Op2>(const double& a, const vec& b) const
{
    std::cout << "Operation 2" << std::endl;
    return 2;
}

int main(int argc, char** argv)
{
    classA<vec> obj;
    vec v {1,2,3};
    obj.foo(1.2, v); // works
    obj.foo<Op1>(1.2, v); // Does not work

   return 0;
}

Note that unfortunately you can't specialise a member function of a templated class so you'd have to add explicit specialisations for each type T for stdOp.

  • Related