Partial specialization of C ++ function template?

I know that the code below is a partial specialization of the class:

template <typename T1, typename T2> class MyClass { โ€ฆ }; // partial specialization: both template parameters have same type template <typename T> class MyClass<T,T> { โ€ฆ }; 

I also know that C ++ does not allow partial specialization of a template template (only completeness is allowed). But does my code mean that I partially specialized my function template for arguments of the same type? Because it works for Microsoft Visual Studio 2010 Express! If not, could you explain the concept of partial specialization?

 #include <iostream> using std::cin; using std::cout; using std::endl; template <typename T1, typename T2> inline T1 max (T1 const& a, T2 const& b) { return a < b ? b : a; } template <typename T> inline T const& max (T const& a, T const& b) { return 10; } int main () { cout << max(4,4.2) << endl;; cout << max(5,5) << endl; int z; cin>>z; } 
+63
c ++ templates template-specialization partial-specialization
Nov 09 2018-11-11T00:
source share
6 answers

In this example, you are actually overloading (not specializing) the function max<T1,T2> . The partial specialization syntax should look slightly lower (if it were allowed):

 //Partial specialization is not allowed by the spec, though! template <typename T> inline T const& max<T,T> (T const& a, T const& b) { ^^^^^ <--- specializing here return 10; } 

[Note: in the case of a function template, only the standard full is allowed by the C ++ standard (excluding compiler extensions).]

+60
Nov 09 '11 at 7:13
source share

Since partial specialization is not allowed - as other answers pointed out - you could get around it with std::is_same and std::enable_if , as shown below:

 template <typename T, class F> inline typename std::enable_if<std::is_same<T, int>::value, void>::type typed_foo(const F& f) { std::cout << ">>> messing with ints! " << f << std::endl; } template <typename T, class F> inline typename std::enable_if<std::is_same<T, float>::value, void>::type typed_foo(const F& f) { std::cout << ">>> messing with floats! " << f << std::endl; } int main(int argc, char *argv[]) { typed_foo<int>("works"); typed_foo<float>(2); } 

Output:

 $ ./a.out >>> messing with ints! works >>> messing with floats! 2 



Change If you need to be able to handle all other remaining cases, you can add a definition that says that cases that have already been processed should not coincide - otherwise you get into ambiguous definitions. The definition may be:

 template <typename T, class F> inline typename std::enable_if<(not std::is_same<T, int>::value) and (not std::is_same<T, float>::value), void>::type typed_foo(const F& f) { std::cout << ">>> messing with unknown stuff! " << f << std::endl; } int main(int argc, char *argv[]) { typed_foo<int>("works"); typed_foo<float>(2); typed_foo<std::string>("either"); } 

What produces:

 $ ./a.out >>> messing with ints! works >>> messing with floats! 2 >>> messing with unknown stuff! either 

Although this whole thing looks a little boring, since you have to tell the compiler everything that you have already done, it is quite possible to process up to 5 or several specialties.

+32
Jan 19 '14 at
source share

What is a specialization?

If you really want to understand patterns, you should take a look at functional languages. The world of templates in C ++ is a purely functional sublanguage.

In functional languages, the choice is made using pattern matching:

 -- An instance of Maybe is either nothing (None) or something (Just a) -- where a is any type data Maybe a = None | Just a -- declare function isJust, which takes a Maybe -- and checks whether it None or Just isJust :: Maybe a -> Bool -- definition: two cases (_ is a wildcard) isJust None = False isJust Just _ = True 

As you can see, we overload the definition of isJust .

Well, C ++ class templates work exactly the same. You provide the main announcement, which indicates the number and nature of the parameters. It can be just an announcement, or it can also act as a definition (your choice), and then you can (if you want) provide template specializations and associate with them another (otherwise it would be stupid) version of the class.

For template functions, specialization is somewhat more inconvenient: it conflicts somewhat with overload resolution. Thus, it was decided that the specialization would relate to the non-specialized version, and specializations would not be taken into account when resolving overloads. Therefore, the algorithm for choosing the right function will look like this:

  • Overload resolution among common functions and non-specialized templates
  • If a non-specialized template is selected, check to see if there is a specialization for it that will better match

(for in-depth study see GotW # 49 )

Thus, the template specialization of functions is a citizen of the second zone (literally). As far as I know, we would be better off without them: I still have to deal with the case when the use of the template cannot be allowed with overloading.

Is this specialization a template?

No, this is just overload, and that's fine. In fact, overloads usually work the way we expect them to, while specializations can be unexpected (remember the article related to GotW).

+12
Nov 09 2018-11-11T00:
source share

No. For example, you can legally specialize std::swap , but you cannot legally define your own overload. This means that you cannot make std::swap work for your own custom class template.

In some cases, overload and partial specialization may have the same effect, but not all.

+4
Nov 09 '11 at 7:09
source share

Non-classical, non-variable partial specialization is not allowed, but, as said:

All problems in computer science can be solved by a different level of indirection. - David Wheeler

Adding a class to forward a function call may solve this, here is an example:

 template <class Tag, class R, class... Ts> struct enable_fun_partial_spec; struct fun_tag {}; template <class R, class... Ts> constexpr R fun(Ts&&... ts) { return enable_fun_partial_spec<fun_tag, R, Ts...>::call( std::forward<Ts>(ts)...); } template <class R, class... Ts> struct enable_fun_partial_spec<fun_tag, R, Ts...> { constexpr static R call(Ts&&... ts) { return {0}; } }; template <class R, class T> struct enable_fun_partial_spec<fun_tag, R, T, T> { constexpr static R call(T, T) { return {1}; } }; template <class R> struct enable_fun_partial_spec<fun_tag, R, int, int> { constexpr static R call(int, int) { return {2}; } }; template <class R> struct enable_fun_partial_spec<fun_tag, R, int, char> { constexpr static R call(int, char) { return {3}; } }; template <class R, class T2> struct enable_fun_partial_spec<fun_tag, R, char, T2> { constexpr static R call(char, T2) { return {4}; } }; static_assert(std::is_same_v<decltype(fun<int>(1, 1)), int>, ""); static_assert(fun<int>(1, 1) == 2, ""); static_assert(std::is_same_v<decltype(fun<char>(1, 1)), char>, ""); static_assert(fun<char>(1, 1) == 2, ""); static_assert(std::is_same_v<decltype(fun<long>(1L, 1L)), long>, ""); static_assert(fun<long>(1L, 1L) == 1, ""); static_assert(std::is_same_v<decltype(fun<double>(1L, 1L)), double>, ""); static_assert(fun<double>(1L, 1L) == 1, ""); static_assert(std::is_same_v<decltype(fun<int>(1u, 1)), int>, ""); static_assert(fun<int>(1u, 1) == 0, ""); static_assert(std::is_same_v<decltype(fun<char>(1, 'c')), char>, ""); static_assert(fun<char>(1, 'c') == 3, ""); static_assert(std::is_same_v<decltype(fun<unsigned>('c', 1)), unsigned>, ""); static_assert(fun<unsigned>('c', 1) == 4, ""); static_assert(std::is_same_v<decltype(fun<unsigned>(10.0, 1)), unsigned>, ""); static_assert(fun<unsigned>(10.0, 1) == 0, ""); static_assert( std::is_same_v<decltype(fun<double>(1, 2, 3, 'a', "bbb")), double>, ""); static_assert(fun<double>(1, 2, 3, 'a', "bbb") == 0, ""); static_assert(std::is_same_v<decltype(fun<unsigned>()), unsigned>, ""); static_assert(fun<unsigned>() == 0, ""); 
+2
Jan 12 '18 at 2:48
source share

The late answer, but some later readers may find it useful: sometimes an auxiliary function - designed so that it can be specialized - can solve the problem too.

So, imagine this is what we were trying to solve:

 template <typename R, typename X, typename Y> void function(X x, Y y) { R* r = new R(x); f(r, y); // another template function? } // for some reason, we NEED the specialization: template <typename R, typename Y> void function<R, int, Y>(int x, Y y) { // unfortunately, Wrapper has no constructor accepting int: Wrapper* w = new Wrapper(); w->setValue(x); f(w, y); } 

OK, specialization of a partial template, we cannot do this ... Therefore, let's "export" the part necessary for specialization to an auxiliary function, specialize in it and use it:

 template <typename R, typename T> R* create(T t) { return new R(t); } template <> Wrapper* create<Wrapper, int>(int n) // fully specialized now -> legal... { Wrapper* w = new Wrapper(); w->setValue(n); return w; } template <typename R, typename X, typename Y> void function(X x, Y y) { R* r = create<R>(x); f(r, y); // another template function? } 

This can be interesting, especially if the alternatives (normal overloads instead of specializations, the workaround suggested by Ruben ... not that they are bad or mine are better, just another one) will share quite a lot of common code.

0
Sep 22 '17 at 8:23
source share



All Articles