Specializing and or Overloading member function templates with variadic parameters

淺唱寂寞╮ 提交于 2019-12-25 01:48:54

问题


Trying to resolve overload resolution for class member: static function template overload - partial specialization.

I currently have a class declared / defined as such:

Note: my use of Param a, Param b, Param c etc. are not related to the actual declarations / definitions directly. These can be any arbitrary type that is passed into the functions for example: it could be int a, enum b, char c. I'm just using this to only show the pattern of the declarations, however all of the different engines take the same 3 different parameters.

SomeEngine.h

#ifndef SOME_ENGINE_H
#define SOME_ENGINE_H

class SomeEngine {
public:
    SomeEngine() = delete;

    static engineA& getEngineA( Param a, Param b, Param c );
    static engineB& getEngineB( Param a, Param b, Param c );
    // ... more static functions to return other engines

    template<class Engine>
    static Engine& getEngine( Param a, Param b, Param c );
};

// Another class

// function template that uses both classes above

#endif // SOME_ENGINE_H

SomeEngine.cpp

#include "SomeEngine.h"

template<>
EngineA& SomeEngine::getEngine( Param a, Param b, Param c ) {
    return getEngineA( a, b, c );
}

template<>
EngineB& SomeEngine::getEngine( Param a, Param b, Param c ) {
    return getEngineB( a, b, c );
}

The above design pattern for the function template where I was able to specialize the class to return the appropriate Engine Type using a single generic getEngine() call compiles and works fine. I have a non class member function template that takes a class Engine as one of its template parameters... This is defined in the same header above outside of any class and after the first two classes that it will use.

template<class Engine, typename T>
T generateVal( Param a, Param b, Param c ) {
    static T retVal = 0;
    static Engine engine = SomeEngine::getEngine<Engine>( a, b , c );
}

And the above works except the function shown here is not complete. It relies on another class. The other class itself has a similar pattern as the one above; it has a deleted default constructor, and a bunch of static methods to return the different types of objects; however in the 2nd class, almost all of the static methods themselves are function templates, some have overloaded versions while others have more than one template parameter. It is also declared in the same header file above. It looks something like this:

class SomeOther {
public:
    SomeOther() = delete;

    template<class IntType = int>
    static otherA<IntType>& getOtherA( IntType a, IntType b );

    template<class RealType = double>
    static otherB<RealType>& getOtherB( RealType a, RealType B );

    template<class IntType = int>
    static otherC<IntType>& getOtherC( IntType a );

    template<class RealType = double>
    static otherD<RealType>& getOtherD( RealType a );

    template<class IntType = int>
    static otherE<IntType>& getOtherE();

    template<class IntType = int>
    static otherE<IntType>& getOtherE( IntType a, IntType b );

    template<class IntType = int>
    static otherE<IntType>& getOtherE( std::initializer_list<double> a );

    template<class IntType = int, class X>
    static otherE<IntType>& getOtherE( std::size_t a, double b, double c, X x );

};

I'm trying to do something similar with the 2nd class above to have a generic function template such that I can pass to it the template parameter class Other except that class Other depends on its own template arguments and the internal function calls may have a different amount of parameters.

This was leading me into the use of variadic templates for the declarations of this class's function.

I had tried something like this:

template< typename Type, 
          template<typename, class...> class Other,
          class... OtherParams, 
          class... FuncParams> 
static Other<Type, OtherParams...>& getOther( FuncParams... params );

And then my function that I've shown above that was not complete I tried this when adding in the support for the 2nd class:

template< class Engine, 
          typename Type, 
          template<typename, class...> class Other,
          class... OtherParams,
          class... FuncParams>
Type generate( Param a, Param b, Param c, FuncParams... params ) {
    static Type retVal = 0;
    static Engine engine = SomeEngine::getEngine<Engine>( a, b, c );
    static Other<Type, OtherParams...> other = SomeOther::getOther<Type, Other<Type, OtherParams...>> ( params... );
    retVal = other( engine );
    return retVal;
}

This is how I would be using the 2nd class above. Here are my attempts of trying to specialize a couple of the getOther() functions in the corresponding cpp file

template<typename Type,
        template<typename, class...> class Other,
        class... OtherParams,
        class... FuncParams>
otherA<Type>& SomeOther::getOther( FP... params ) {
    return getOtherA( params... );
}

template<typename Type,
         template<typename, class...> class Other,
         class... OtherParams,
         class... FuncParams>
otherB<Type>& SomeOther::getOther( FP... params ) {
     return getOtherB( params... );
}

This doesn't compile it complains that the function definition does not match an existing declaration. I even tried to write an overload in the header file and I keep getting the same errors. I don't know if it is a syntax error or not. I've tried far to many things to list here, I've searched all over the place looking for something similar but can not seem to find anything relevant.

I would like to know if something like this can be done; and if so what needs to be changed above in order for it to at least compile and build; so I can start to test it during runtime; then move on to add other existing types.

I would like to try and keep the same design pattern of the first class with the 2nd. My stand alone function template is the function that will be called and depending on it's template parameters; it should know which engine - other types to call.


回答1:


In you second case, actually you are not trying to partial specialize getOther. Partial specialization should be written like this:

class Tmp
{
public:
template<class A, class B>
void bar(A a, B b) {}

};


template<class A>
void Tmp::bar<A, int>(A a, int b) {}

and GCC will give an error saying:

error: non-class, non-variable partial specialization 'bar<A, int>' is not allowed
 void Tmp::bar<A, int>(A a, int b) {}

So in your second case, you are actually implementing the method that you declared before and the compiler fails to find a matching declaration.

In order to solve this issue, as partial specialization is not allowed by C++, you can use Function Overloading. Here is an example (it compiles on GCC but fails to compile on VS2017, more like an issue of MSVC but I'm not sure):

template <class T>
class otherA 
{
    T t;
public:
    otherA(T t) : t(t) {}

    void WhoAmI() { cout << "I'm OtherA" << endl; }

    T getValue() { return t; }
};

template <class T, class X>
class otherE 
{
    T t;
public:
    otherE(T t) : t(t) {}

    void WhoAmI() { cout << "I'm OtherE" << endl; }

    T getValue() { return t; }
};

class SomeOther {
public:
    SomeOther() = delete;

    template<class IntType = int>
    static otherA<IntType>& getOtherA(IntType a, IntType b) 
    {
        static otherA<IntType> A(a);
        return A;
    }

    template<class IntType = int, class X>
    static otherE<IntType, X>& getOtherE(IntType a, double b, double c, X x)
    {
        static otherE<IntType, X> E(a);
        return E;
    }

    template<template<typename, class...> class Other,
        class Type,
        class... OtherParams,
        class... FuncParams>
    static Other<Type, OtherParams...>& getOther(FuncParams... params)
    {
        return getOther<Type, OtherParams...>(params...);
    }

private:
    /// Function Overloading

    template<class T,
        class... FuncParams>
    static otherA<T>& getOther(FuncParams... params)
    {
        return getOtherA<T>(params...);
    }

    template<class T,
        class X,
        class... FuncParams>
    static otherE<T, X>& getOther(FuncParams... params)
    {
        return getOtherE<T, X>(params...);
    }
};

template<
    class Type,
    template<typename, class...> class Other,
    class... OtherParams,
    class... FuncParams>
Type test(FuncParams... params) {
    static Other<Type, OtherParams...>& other = SomeOther::getOther<Other, Type, OtherParams...>(params...);
    other.WhoAmI();
    return other.getValue();
}

class foo{};

int main()
{
    int AValue = test<int, otherA>(1, 2);
    cout << "AValue: " << AValue << endl;
    int EValue = test<int, otherE, foo>(3, 2.1, 2.2, foo());
    cout << "EValue: " << EValue << endl;
    return 0;
}

and the output will be

I'm OtherA
AValue: 1
I'm OtherE
EValue: 3


来源:https://stackoverflow.com/questions/49374084/specializing-and-or-overloading-member-function-templates-with-variadic-paramete

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!