开发者

explicit specialization of template class member function

开发者 https://www.devze.com 2023-02-21 01:53 出处:网络
I need to specialize template member function for some type (let\'s say double). It works fine while class X itself is not a template class, but when I make it template GCC starts giving compile-time

I need to specialize template member function for some type (let's say double). It works fine while class X itself is not a template class, but when I make it template GCC starts giving compile-time errors.

#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();
}

here is the error message

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()

What is the problem here, and how can I fix it?


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 if constexpr 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;
  }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消