Skip to content

Last updated:2026-10-12

C++ Prefer Class Templates over Function Templates

https://stackoverflow.com/questions/5688355/partial-specialisation-of-member-function-with-non-type-parameter

I recently came across a very interesting template question — a good test of how well you have mastered the basics of function templates versus class templates. What goes wrong with the example below?

How would you usually fix it?

template <typename T, int R>
struct foo {
  foo(const T& v) : value_(v) {}

  void bar() {
    std::cout << "Generic" << std::endl;
    for (int i = 0; i < R; ++i) std::cout << value_ << std::endl;
  }

  T value_;
};

template <>
void foo<float, 3>::bar() {
  std::cout << "Float" << std::endl;
  for (int i = 0; i < 3; ++i) std::cout << value_ << std::endl;
}

template <int R>
void foo<double, R>::bar() {
  std::cout << "Double" << std::endl;
  for (int i = 0; i < R; ++i) std::cout << value_ << std::endl;
}

The answer: the last two specializations simply do not compile.

(1) The first, template <> void foo<float, 3>::bar(), is a full specialization — no problem there;

(2) the second, template <int R> void foo<double, R>::bar(), attempts a partial specialization — but partial specialization of function templates is not allowed! The compiler parses it as a new function template overload, the signature does not match, and it errors out.

The Fix: Enter Class Templates

The C++ rule is: function templates can only be fully specialized, never partially; class templates can do both. So the idiom is to pull "logic that must branch on types" out of the function template into a class template (usually a trait/policy) and partially specialize the class:

template <typename T, int R>
struct bar_impl {
  static void run(foo<T, R>& f) {
    std::cout << "Generic" << std::endl;
    for (int i = 0; i < R; ++i) std::cout << f.value_ << std::endl;
  }
};

template <int R>
struct bar_impl<double, R> {  // 类模板偏特化:合法!
  static void run(foo<double, R>& f) {
    std::cout << "Double" << std::endl;
    for (int i = 0; i < R; ++i) std::cout << f.value_ << std::endl;
  }
};

template <typename T, int R>
void foo<T, R>::bar() { bar_impl<T, R>::run(*this); }

This is also standard practice in the STL: iterator_traits, __type_traits — all of them follow the "class template carries partial specializations" pattern.

Summary

(1) Function templates: overloading + full specialization only, no partial specialization;

(2) when you need "a special implementation for a certain family of types", move the implementation into a class template and dispatch via partial specialization/traits;

(3) since C++11, many cases can branch directly inside the function body with if constexpr, skipping the class template entirely — but its trade-off versus partial specialization is left for you to ponder.

Comments