3

Why does C++ forbid this partial specialization?

What kind of philosophy is behind this forbiddance, so I can accept it?
Programming would be much easier without this forbiddance.

Templates shall prevent redundances.
Now I have to produce redundance for every class N with int d = 3.

//__________________________________________
//__________________________________________
//
template<class N, int d>
class MyClass
{
public:
    void doo();
};

//__________________________________________
//__ allowed _______________________________
//
template<class N, int d>
void MyClass<N, d>::doo()
{
    cout << "general";
}

//__________________________________________
//__ forbidden _____________________________
//
template<class N>  
void MyClass<N, 3>::doo()
{
    cout << "partial specialization";
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
MBastieK
  • 31
  • 3
  • 2
    You are trying to partially specialize a function. C++ only allows partial specialization of classes. As for the rationale, presumably it is because you can use a partially specialized class to do anything a function could have, and the language becomes much easier to compile when partial specialization is restricted to only classes. – Ben Voigt Sep 17 '21 at 17:58
  • 2
    With C++17 you could have an `if constexpr (d == 3)` inside the implementation of `doo` as a way to "specialize" only a single function – UnholySheep Sep 17 '21 at 18:00
  • @UnholySheep: Sorry, I can't accept this answer. Templating shall provide clean coding, and with this workaround my code becomes unclean. Bloated.


    But thanks for answering anyway.
    – MBastieK Sep 17 '21 at 18:20
  • @MBastieK Well, I may have missed something, but I'm not sure to understand how could you consider the code in this [answer](https://stackoverflow.com/a/69227930/4944425) *more* bloated than the others. It's a matter of opinion, I guess. – Bob__ Sep 17 '21 at 18:29
  • @Bob__ I follow Robert C. Martin philosophy. – MBastieK Sep 17 '21 at 18:33
  • @BenVoigt: "the language becomes much easier to compile when partial specialization is restricted" This answer I kind of accept. Much easier for compiler-programmers. "because you can use a partially specialized class to do anything a function could..." This produces a lot of redundances, if I dont want to inherit. Lot of workarounds for this forbiddance I dont accept till I understand its philosophy. – MBastieK Sep 17 '21 at 18:49
  • @MBastieK: Not only easier for compiler-programmers, a language with restrictions is also easier for the maintenance programmer who has to come and read the code you wrote. – Ben Voigt Sep 17 '21 at 19:04
  • 1
    The philosophy? https://stackoverflow.com/q/13923684/817643 - Your are asking for *implicit* partial specialization. That's adding oil to a language that can already [self immolate](https://timsong-cpp.github.io/cppwp/n4868/temp.expl.spec#8.sentence-2). – StoryTeller - Unslander Monica Sep 17 '21 at 19:22
  • @BenVoigt:Yes, but my first solution in initial-question is an inutuitiv and not complex code to read. But maybe Stroustrup sees a philosophy behind it why he forbids this inutuitiv solution. Assembling partial specializations is intuitiv. C++ allows a lot, its open(I dont know the name of this open principle now). But here it forbids something, which is intuitiv in my opinion. I dont find good books about templating complex templates. – MBastieK Sep 17 '21 at 19:23
  • @MBastieK: But that code is the same code for a non-template member function of a partially specialized class. So when a programmer (or compiler) reads it, that's what they have to assume. – Ben Voigt Sep 17 '21 at 19:28
  • @StoryTeller-UnslanderMonica Thats sounds like something more profound. I will take my time to read it. – MBastieK Sep 17 '21 at 19:30
  • @BenVoigt Sorry, I dont see the ambuigity now and in my example. But maybe I have to eat something. There is no ambuigity in my example, i am sure, but maybe in complex projects. – MBastieK Sep 17 '21 at 19:39
  • @MBastieK: The last 5 lines of your question and the last 5 lines of Vlad's answer are **identical**. In your question, you thought it should be a partially specialized member function of the unspecialized class template. In Vlad's answer, those exact same 5 lines are a member function of a partial specialization of the class template. The "intuitive" code you tried to write already has a meaning, it cannot have both its existing meaning and the meaning you wish to give it. – Ben Voigt Sep 17 '21 at 19:42

4 Answers4

7

You need at first to partially specialize the class itself. It is the class that is a template. Its member function is a non-template function. Partial specializations of a class can differ in their definitions. That is they can have different sets of members.

For example

template<class N, int d>
class MyClass
{
public:
    void doo();
};

template<class N, int d>
void MyClass<N, d>::doo()
{
    std::cout << "general";
}

template<class N>
class MyClass<N, 3>
{
public:
    void doo();
};

template<class N>  
void MyClass<N, 3>::doo()
{
    std::cout << "partial specialization";
}

As for function templates then they have their own mechanism of function overloading and function specializations.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thanks for your answer. (I use to stumble upon this a lot, I relied too much in specialization). I always wondered though if there is a way to avoid repeating (basically) the implementation of `MyClass` which will look in general very similar to `MyClass` in your case. Maybe there is a way using inheritance (for composition). Do you know of any such technique? – alfC Sep 17 '21 at 18:21
  • @Vlad from Moscow:Specialization of the first class produces a lot of redundance,if the first class has more content in it and if I dont want to inherit. I dont care,if the compiler produces a new specialized complete class with my first solution.Thats the nature of templating.The reason why I ask this question in stackoverflow was, because I had a problem with an recursive integral template member function in a templ.class.But it would be to much for now.I dont accept that forbiddance of my first solution in my initial question until I see a logic reason or philosophy,which I dont till now. – MBastieK Sep 17 '21 at 19:03
5

You can partially specialize the class first, then implement the method for the specialized class:

#include <iostream>

template<class N, int d>
class MyClass
{
public:
    void doo();
};

template<class N>
class MyClass<N, 3>
{
public:
    void doo();
};

template<class N, int d>
void MyClass<N, d>::doo()
{
    std::cout << "general\n";
}

template<class N>
void MyClass<N, 3>::doo()
{
    std::cout << "partial specialization\n";
}

int main()
{
    MyClass<int, 1> o1{}; o1.doo();
    MyClass<int, 3> o3{}; o3.doo();
}

// Outputs
//     general
//     partial specialization

Demo

rturrado
  • 7,699
  • 6
  • 42
  • 62
  • What if the first class has more content into it? Do I have to copy-paste, if I dont want to inherit? And i just want to understand the philosophy or rule behind it. For now i dont see any problems in my first solution. The compiler could put together (assemble) a specialized class with my first solution. – MBastieK Sep 17 '21 at 18:52
  • @MBastieK: Note that the code in this answer for `MyClass::doo()` is precisely the same as in your question.... there would be no way for the compiler to tell whether you were declaring a partially specialized member function of an unspecialized class, a member function of a partially specialized class, a partially specialized member function of a fully specialized class, a partially specialized member function of a partially specialized class.... – Ben Voigt Sep 17 '21 at 19:01
  • The language rule that only classes can be partially specialized is what breaks that ambiguity. – Ben Voigt Sep 17 '21 at 19:02
  • @BenVoigt "there would be no way for the compiler to tell whether.." I see ways. And I dont see ambiguity. But maybe I have to understand your answer more detailed or have a more complicated project. But my initial-question-code could assemble to a completed specialized class. – MBastieK Sep 17 '21 at 19:13
  • @MBastieK Think of it this way. You have a class template and you instantiate a concrete class A from it. You have a specialization for your class template and you instantiate a class B from it. Now you have two concrete classes, A and B, that have no relation between them. You want to avoid code duplication? Why not inherit from a base class. You want new functionality in your B class? Just add it: https://godbolt.org/z/sah6v7oed – rturrado Sep 17 '21 at 19:55
  • 1
    @rturrado "Now you have two concrete classes, A and B," I understand that already, and " Why not inherit from a base class. " I understand too. Like I said, I want to understand the philosophy or rule. I am not interested in workarounds now. But thanks. To see the assembler-code there is an nice thing. Thanks for that. – MBastieK Sep 17 '21 at 20:37
2

Partial specialization of function is a very very tricky thing, especially if you allow overloading the function.

It can lead to unspecializable functions, or specialization that happens on unexpected overload, or specialization can cannot ever be called.

My best advice would be to stay away from function specialization.

And for your problem, I would say, the best would be to simply use C++17 if constexpr:

template<class N, int d>
void MyClass<N, d>::doo()
{
    if constexpr (d != 3) {
        cout << "general";
    } else {
        cout << "only when d == 3";
    }
}
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • This bloats my code. I thought templating is also for clean coding. But thanks for the answer. "It can lead to unspecializable functions, or specialization that happens on unexpected overload, or specialization can cannot ever be called." This sound less like a rule and more like a subjective judgement. Is there a strict rule or philosophy behind this forbiddance? The compiler could put together (assemble) a specialized class with my first solution. – MBastieK Sep 17 '21 at 18:53
  • 1
    @MBastieK I'm not sure what you mean by bloat my code. Visually? That's subjective, specialization look like bloat for me. Bloating binary? No it does not, since the branch is done at compile time. `This sound less like a rule and more like a subjective judgement` Well I listed you the consequences of allowing partial specialization of functions. The committee and compiler implementers decided that the confusion and complication of the language was not worth it, as other things can very well solve the problem, in a simpler manner. – Guillaume Racicot Sep 17 '21 at 20:05
  • `Guillaume Racicot` Yes subjective. I follow Robert C. Martin philosophy. `The committee and compiler implementers decided that the confusion and complication of the language was not worth it` Yes, that I want to understand more nuanced or differentiated. `StoryTeller-UnslanderMonica` gave me a presumably profound answer there, which I will take time to read. Thanks. – MBastieK Sep 17 '21 at 21:45
2

I used to rely on pattern matching a lot which made me prone to this limitation of the language very often, for which I never found a elegant practical solution. I guess I instinctively learned to steer away from this kind of design, although I still stumble upon it once in while.

There are four solutions that I know:

  1. The modern solution (if constexpr), (practical but not elegant IMO).
  2. The classical solution (not practical in real world).
  3. The classical solution + CRTP (practical but elegant?)
  4. The "esoteric" solution, enable_if (not elegant IMO)

This post elaborates mostly about number 3.

None of these solution are 100% satisfying to me. I don't see a fundamental reason why the language couldn't just specialize the template class when it encounters a specialized member function declaration, at least with limitations.


The modern solution (shown in one answer) is to use if constexpr in the general version. I find this like cheating and it is not elegant and I agree with you that there must be a way to do this within templates.


The classical solution of course is what it is shown in the other answers too. However I think it is not practical, the reason is that MyClass<N, 3> typically looks very similar in its declarations and implementation to the general case MyClass<N, d> therefore one has to repeat the whole implementation of MyClass for each case (partially) specialized. This is unacceptable to me.

For example, consider what happens if you have a more complex class:

template<class N, int d>
class MyClass
{
public:
    void A() const{....}
    void B() const{....}
    .
    .
    .
    void Z() const{....}
    void doo(){std::cout << "general";}
};

Not necessarily, but chances are that you will need to also have a lot of repeated code:

template<class N>
class MyClass<N, 3>
{
    void A() const{....}
    void B() const{....}
    .
    .
    .
    void Z() const{....}
    void doo();
};

I cannot think a worst case of DRY (Don't repeat yourself) violation.


The solution I found is to extract the absolute common parts of MyClass:

template<class MyClassCRTP>
class BasicMyClass<MyClassCRTP> // eventually we need will need to know the derived class
{
public:
    void A() const{....}
    void B() const{....}
    .
    .
    .
    void Z() const{....}
};

template<class N, int d>
class MyClass : BasicMyClass<MyClass<N, d>>
{
public:
    void doo(){std::cout << "general";}
};

template<class N>
class MyClass<N, 3> : BasicMyClass<MyClass<N, 3>>
{
    void doo();
};

And now you can specialize MyClass<N, 3>::doo without much repeated code.

Is this really elegant? I don't know. It does also open other cans of worms. Not ideal certainly, we wanted to specialize a (method) function and we ended up with extra classes!


Finally, for completeness, the esoteric solution, not better than if constexpr but somewhat backward compatible IMO:

template<class N, int d>
class MyClass
{
public:
    template<class Dummy, std::enable_if<d != 3 and sizeof(Dummy*), int> = 0>
    void doo(){std::cout << "general";}
    template<class Dummy, std::enable_if<d == 3 and sizeof(Dummy*), int> = 0>
    void doo(){std::cout << "special";}
};
alfC
  • 14,261
  • 4
  • 67
  • 118
  • @`alfC` `I agree with you that there must be a way to do this within templates.` Thanks. My original problem was with a template class with one class integral template parameter and within a recursive template function with one other integral template parameter also, and I needed an end-function for integral = 0. Without the class integral template parameter I had no problems. Could even use the same object instantiation with different function instantiation, which I hoped used loop-unfolding or more correct recursive-unfolding, which maybe could be named tailoring (while compile-time). – MBastieK Sep 18 '21 at 07:37
  • @`alfC` Appendix: Meta-programming in meta-programming with 'if constexpr' corrupts the template-principle for me. – MBastieK Sep 18 '21 at 07:39