模板类成员函数的显式专用化

我需要为某种类型专门化模板成员函数(比方说 双倍)。当类 X本身不是一个模板类时,它工作得很好,但是当我使用它时,模板 GCC 开始出现编译时错误。

#include <iostream>
#include <cmath>


template <class C> class X
{
public:
template <class T> void get_as();
};


template <class C>
void X<C>::get_as<double>()
{


}


int main()
{
X<int> x;
x.get_as();
}

下面是错误消息

source.cpp:11:27: error: template-id
'get_as<double>' in declaration of primary template
source.cpp:11:6: error: prototype for
'void X<C>::get_as()' does not match any in class 'X<C>'
source.cpp:7:35: error: candidate is:
template<class C> template<class T> void X::get_as()

我该怎么补救,这里有什么问题吗?

先谢谢你。

104826 次浏览

It doesn't work that way. You would need to say the following, but it is not correct

template <class C> template<>
void X<C>::get_as<double>()
{


}

Explicitly specialized members need their surrounding class templates to be explicitly specialized as well. So you need to say the following, which would only specialize the member for X<int>.

template <> template<>
void X<int>::get_as<double>()
{


}

If you want to keep the surrounding template unspecialized, you have several choices. I prefer overloads

template <class C> class X
{
template<typename T> struct type { };


public:
template <class T> void get_as() {
get_as(type<T>());
}


private:
template<typename T> void get_as(type<T>) {


}


void get_as(type<double>) {


}
};

If one is able to used std::enable_if we could rely on SFINAE (substitution failure is not an error)

that would work like so (see LIVE):

#include <iostream>
#include <type_traits>


template <typename C> class X
{
public:
template <typename T,
std::enable_if_t<!std::is_same_v<double,T>, int> = 0>
void get_as() { std::cout << "get as T" << std::endl; }


template <typename T,
std::enable_if_t<std::is_same_v<double,T>, int> = 0>
void get_as() { std::cout << "get as double" << std::endl; }
};


int main() {
X<int> d;
d.get_as<double>();


return 0;
}

The ugly thing is that, with all these enable_if's only one specialization needs to be available for the compiler otherwise disambiguation error will arise. Thats why the default behaviour "get as T" needs also an enable if.

Probably the cleanest way to do this in C++17 and on-wards is to use a ABC0 in combination with the std::is_same_v type trait without explicitly specialisation at all:

#include <iostream>
#include <type_traits>


template <typename C>
class X {
public:
template <typename T>
void get_as() {
// Implementation part for all types
std::cout << "get as ";


// Implementation part for each type separately
if constexpr (std::is_same_v<double, T>) {
std::cout << "'double'";
} else if constexpr (std::is_same_v<int, T>) {
std::cout << "'int'";
} else {
std::cout << "(default)";
}


// Implementation part for all types
std::cout << std::endl;
return;
}
};


int main() {
X<int> d {};
d.get_as<double>(); // 'double'
d.get_as<int>();    // 'int'
d.get_as<float>();  // (default)


return EXIT_SUCCESS;
}

Try it here!


If you need to have a return type as well you could declare the return type as auto:

template <typename T>
auto get_as() {
if constexpr (std::is_same_v<double, T>) {
return 0.5;
} else {
return 0;
}
}