Home > Software engineering >  Why does C forbid this partial specialization?
Why does C forbid this partial specialization?

Time:09-22

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";
}

CodePudding user response:

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.

CodePudding user response:

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

CodePudding user response:

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";
    }
}

CodePudding user response:

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";}
};
  • Related