5

I want to write the following:

template <typename S, typename T> void foo() {
    /* code for the general case */
}

template <typename T> void foo<MySType,T>() {
    /* partially specialized code - for any kind of T, but when S is MySType */
}

or, in other cases, the following:

template <typename S, typename T> void bar(const S& a, const T& b) {
    /* code for the general case */
}

template <typename T> void bar<MySType,T>(const MySType& a, const T& b) {
    /* partially specialized code - for any kind of T, but when S is MySType */
}

C++(11) won't let me do this.

Now, I read this question and its answers; let's assume I buy the explanation of why we don't have partial template specialization (or just assume that that I'm living in reality and actually want to write code). Well, what do I do, in stead?

I would really rather not wrap these functions in a class unless that's absolutely my last resort.

Community
  • 1
  • 1
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • honestly the more I dive into C++11 and C++ in general, the more I think that "template specialization" is an horrible qualification for what is happening. There is no such thing as "template specialization" – user2485710 May 03 '14 at 07:24
  • @user2485710: I'm sorry, I don't understand... can you elaborate? Or link to where you've argued this at more length? – einpoklum May 03 '14 at 07:25
  • 1
    Not only is template specialization a thing, it's a Turing-complete machine. It can drive you mad, though. – Potatoswatter May 03 '14 at 07:28
  • "specializing" means that you are enhancing the properties of something that already exist, what a template signature is able to perform is type inference or type deduction, to create the functions that you need. To specialize things you need at least 1 thing that already exist plus at least 1 properties to enhance/change, I never get why someone would call this "template specialization" when in fact there is just some type inference and type deduction. Someone even calls this "overload" which is something that only happens at runtime. – user2485710 May 03 '14 at 07:30
  • @user2485710: Umm, I would think this is specialization since we're treating a special case differently than the general case, that's all. – einpoklum May 03 '14 at 07:34
  • @einpoklum this functions don't even exist until the compiler process them, and the compiler doesn't see this as specialization, it just fills the right labels with the right type. – user2485710 May 03 '14 at 07:36
  • 2485710, no. Overload resolution happens at compile time. – Yakk - Adam Nevraumont May 03 '14 at 08:00

3 Answers3

5

Overload! Overloading is superior in every way to specialization. Part of overload resolution is picking the most specialized overload. Just declare the "specializations" as overloads and it will work if partial specialization would have.

Avoid explicit template arguments, though. You might use tag dispatching instead.

template< typename t >
struct tag {};

template <typename S, typename T> foo( tag<S>, tag<T> ) {
    /* code for the general case */
}

template <typename T> foo( tag<MyType>, tag<T> ) {
    /* partially specialized code - for any kind of T, but when S is MyType */
}

Since the tags are empty and passed by value, their contribution to function call overhead may be eliminated by the compiler.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • The OP's example is poorly constructed (missing return types, etc), but brings an interesting twist. Mainly, how do you overload something that has *no parameters*? – WhozCraig May 03 '14 at 07:25
  • @Potatoswatter: So, if S or T are not represented in parameter types, you suggest adding tag parameters to enable overloading. Would you suggest using these also when S and T do correspond to parameters? – einpoklum May 03 '14 at 07:30
  • @einpoklum no worries. It didn't detract for the meaningfulness of the question itself. And tag dispatching isn't necessarily intuitive as a solution if you're engrained in specialization of the class-type variety, so don't think it odd you didn't consider it out of the gate. – WhozCraig May 03 '14 at 07:31
  • @einpoklum If S and T are deduced from parameters, then the tags will only cause troublesome redundancy — the parameter and tag types would have to match or you get an error. Fortunately there's seldom overlap between real parameters and tags. – Potatoswatter May 03 '14 at 07:36
  • @einpoklum and answering your comment, as soon as you start throwing parameters into the mix you can start using real overloads. [See it live](http://ideone.com/9FA2er), but you likely already knew that. – WhozCraig May 03 '14 at 07:37
  • @WhozCraig: Mind making that into an answer? – einpoklum May 03 '14 at 07:57
3

Another option is to use a helper class template, where you can do partial specialization, and hide it using a wrapper function which doesn't need to be partially specialized itself:

#include <iostream>

template<typename S, typename T>
struct foo_helper {
    void foo() {
        std::cout << "general func" << std::endl;        
    }
};

struct MyType {};

template <typename T>
struct foo_helper <MyType,T> {
    void foo() {   
        std::cout << "partially specialized code - for any kind of T, but when S is MyType" << std::endl;
    }
};

template<typename S, typename T>
void foo() {
    foo_helper<S, T>().foo();
}

int main () {
    foo<int, int>();
    foo<int, double>();
    foo<MyType, long>();
}

This is valid C++98/03 as well.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
chill
  • 16,470
  • 2
  • 40
  • 44
2

You might partial specialize a helper structure:

#include <iostream>

namespace Detail {
    template <typename S, typename T>
    struct Foo {
        static void apply() {
            std::cout << "general case\n";
        }
    };
    template <typename T>
    struct Foo<int, T> {
        static void apply() {
            std::cout << "specialized code\n";
        }
    };
}

template <typename S, typename T>
void foo() {
    Detail::Foo<S, T>::apply();
}

int main()
{
    foo<double, double>();
    foo<int, double>();
    return 0;
}
  • Can this be automated/streamlined somehow, so that I don't have to always explicitly write in the namespace, the structs et cetera? – einpoklum May 12 '14 at 12:45