C++ - Overload templated class method with a parti

2020-08-01 08:58发布

问题:

There are a few questions already similar to this already on stack overflow, but nothing that seemd to directly answer the question I have. I do apologise if I am reposting.

I'd like to overload a few methods of a templated class (with 2 template parameters) with a partial template specialisation of those methods. I haven't been able to figure out the correct syntax, and am starting to think that it's not possible. I thought I'd post here to see if I can get confirmation.

Example code to follow:

template <typename T, typename U>
class Test
{
public:
    void Set( T t, U u ); 

    T m_T;
    U m_U;
};

// Fully templated method that should be used most of the time
template <typename T, typename U>
inline void Test<T,U>::Set( T t, U u )
{
    m_T=t;
    m_U=u;
}

// Partial specialisation that should only be used when U is a float.
// This generates compile errors
template <typename T>
inline void Test<T,float>::Set( T t, float u )
{
    m_T=t;
    m_U=u+0.5f;
}


int _tmain(int argc, _TCHAR* argv[])
{
    Test<int, int> testOne;    
    int a = 1;
    testOne.Set( a, a );

    Test<int, float> testTwo;    
    float f = 1.f;
    testTwo.Set( a, f );
}

I know that I could write a partial specialisation of the entire class, but that kinda sucks. Is something like this possible?

(I'm using VS2008) Edit: Here is the compile error error C2244: 'Test::Set' : unable to match function definition to an existing declaration

Thanks :)

回答1:

The particular problem you're sketching is easy:

template< class T >
inline T foo( T const& v ) { return v; }

template<>
float foo( float const& v ) { return v+0.5; }

Then call foo from your Test::Set implementation.

If you want the full generality, then similarly use a helper class with static helper member functions, and partially specialize that helper class.

Cheers & hth.,



回答2:

You cannot partially specialize a member function without defining partial specialization of the class template itself. Note that partial specialization of a template is STILL a template, hence when the compiler sees Test<T, float>, it expects a partial specialization of the class template.

--

$14.5.4.3/1 from the C++ Standard (2003) says,

The template parameter list of a member of a class template partial specialization shall match the template parameter list of the class template partial specialization. The template argument list of a member of a class template partial specialization shall match the template argument list of the class template partial specialization. A class template specialization is a distinct template. The members of the class template partial specialization are unrelated to the members of the primary template. Class template partial specialization members that are used in a way that requires a definition shall be defined; the definitions of members of the primary template are never used as definitions for members of a class template partial specialization. An explicit specialization of a member of a class template partial specialization is declared in the same way as an explicit specialization of the primary template.

Then the Standard itself gives this example,

// primary template
template<class T, int I> struct A {
void f();
};
template<class T, int I> void A<T,I>::f() { }

// class template partial specialization
template<class T> struct A<T,2> {
void f();
void g();
void h();
};
// member of class template partial specialization
template<class T> void A<T,2>::g() { }

I hope the quotation from the Standard along with the example answers your question well.



回答3:

There's also another solution to the partial specialization problem, if you don't want to introduce additional functions, methods or classes to your code.

#include <type_traits>

template <typename T1, typename T2>
class C
{
    void f(T1 t1);
}

template <typename T1, typename T2>
void C<T1, T2>::f(T1 t1)
{
    if (std::is_same<T2, float>::value)
    // Do sth
    else
    // Do sth
}