16

Hallo!

I would like to specialise only one of two template types. E.g. template <typename A, typename B> class X should have a special implementation for a single function X<float, sometype>::someFunc().

Sample code:

main.h:

#include <iostream>

template <typename F, typename I>
class B
{
public:
    void someFunc()
    {
        std::cout << "normal" << std::endl;
    };

    void someFuncNotSpecial()
    {
        std::cout << "normal" << std::endl;
    };
};

template <typename I>
void B<float, I>::someFunc();

main.cpp:

#include <iostream>
#include "main.h"

using namespace std;

template <typename I>
void B<float, I>::someFunc()
{
    cout << "special" << endl;
}

int main(int argc, char *argv[])
{
    B<int, int> b1;
    b1.someFunc();
    b1.someFuncNotSpecial();

    B<float, int> b2;
    b2.someFunc();
    b2.someFuncNotSpecial();
}

Compilation fails for class B. Is it true, that this is not possible in C++ in this way? What would be the best workaround?

[edit]

template <float, typename I> void B<float, I>::someFunc(); leads to main.h:26: error: ‘float’ is not a valid type for a template constant parameter

template <typename I> void B<float, I>::someFunc(); leads to main.h:27: error: invalid use of incomplete type ‘class B’

And I'm using gcc.

[edit]

I don't want to specialise the whole class, as there are other functions that don't have a specialisation.

tauran
  • 7,986
  • 6
  • 41
  • 48
  • Is the class template A related to your question? – Doug Sep 22 '10 at 11:51
  • I thought it would make the question easier to understand. I will remove it. – tauran Sep 22 '10 at 11:53
  • This has been asked hundreds of times on stackoverflow :) I think some of us could set up a real template FAQ with such questions. People can check the faq to see whether their question is answered, instead of having to search for a dupe. – Johannes Schaub - litb Sep 23 '10 at 04:53
  • @Johannes : Why don't you write such articles(FAQs)? I think you are one of the best when it comes to `templates` in C++. :-) – Prasoon Saurav Sep 24 '10 at 02:20

4 Answers4

25

You have to provide a partial specialization of the class template B:

template <typename I>
class B<float, I>
{
public:
    void someFunc();
};

template <typename I>
void B<float, I>::someFunc()
{
    ...
}

You can also just define someFunc inside the specialization.

However, if you only want to specialize a function, and not a class do e. g.

template <typename F, typename I>
void someFunc(F f, I i) { someFuncImpl::act(f, i); }

template <typename F, typename I>
struct someFuncImpl { static void act(F f, I i) { ... } };

// Partial specialization
template <typename I>
struct someFuncImpl<float, I> { static void act(float f, I i) { ... } };

But you can't specialize a function template without this trick.

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • 1
    You couldn't know this. But in my use case there are lots of other functions that don't have a specialisation. With this approach I would have to double all these functions. – tauran Sep 22 '10 at 12:02
  • 1
    @tauran: You cannot provide partial specializations for function templates. Only for class templates, and you have to provide the definition again for the whole class. Live with it, or see updated answer. – Alexandre C. Sep 22 '10 at 12:04
  • Could you please address one more thing in tauran's code? There is `template void B::someFunc();` at the end of `main.h`. I think tauran wants to declare, that there is a specialization defined somewhere, in another compilation unit. Doesn't such thing require exported templates? If the specialization is meant, to be visible in all compilation units, `main.h` is included in, doesn't it have to be in the header? – Maciej Hehl Sep 22 '10 at 12:23
  • @Maciej: Exported template don't exist (except in the standard). Everything which is not an explicit (full) specialization has to be declared in the header. – Alexandre C. Sep 22 '10 at 12:49
  • @Maciej it's worse because his attempt tries to declare a member function of a class template partial specialization `B`. Such a partial specialization does not exist, so that thing at the end of `main.h` is ill-formed. (Even if it would exist, it would be ill-formed because an out-of-class declaration of a function must be a definition). – Johannes Schaub - litb Sep 24 '10 at 14:12
  • Are you sure about that? Maybe I made an error but the last time I did it, I ended up putting the declaration for a specialized function in the .h file and the definition in a .cpp file (gcc). gcc would not let me put the specialized function in the .h file and without the delaration there it would ignore the specialized version in the .cpp file... – tauran Sep 24 '10 at 18:12
  • @AlexandreC. In you last code block, the case of specializing a function but not a class, could not `someFunc()` just be [overloaded](https://www.fluentcpp.com/2017/08/15/function-templates-partial-specialization-cpp/)? There would be the `template void someFunc(F f, I i){ ... }` and the overload to handle `float` would be `template void someFunc(float f, I i){ ... }`. I managed to make it work. Even swapping declaration order or the "specialized" argument position did not affect it (with GCC). Altough not specialization, it should serve the same purpose. – Dudly01 Apr 04 '21 at 23:12
7

Although you can totally specialize member functions of a class template, you cannot _partially specialize member functions. - Andrei Alexandrescu

Partial Class specialization is explained by the other posters.

You can, however, use overloading:

template <class T, class U> T fun(U obj); // primary template
template <class U> void Fun<void, U>(U obj); // illegal pertial
// specialization
template <class T> T fun (Window obj); // legal (overloading)

If you want to go deep into this, you can read about this issue in depth in "Modern C++ Design" by A. Alexandrescu.

Gabriel Schreiber
  • 2,166
  • 1
  • 20
  • 33
  • +1 for recalling template overloads. This is a tool I seldom think about using, but which does the job in some situations. – Alexandre C. Sep 22 '10 at 12:12
0

Solution 1. move all implementation to a base class like B_Base. then specialize on float to override the someFunc. like below

    template <typename F, typename I>
    class B : B_Base<F, I>
    {
    }

    template <typename I>
    class B<float, I> : B_Base<flat, I>
    {
    public:
        void someFunc() {....}
    };

Solution 2. use function overload, put float as input or boost::is_same to dispatch. unfortunately, you function someFunc doesn't have parameter. so it need change the interface.

roalz
  • 2,699
  • 3
  • 25
  • 42
fatbone
  • 1
  • 1
0

I think C++ expert Mayer provides us an elegant function. Cons is it depends on GCC feature.

#include <map>
#include <iostream>
#include <string>

using namespace std;


template<typename... T>
void TemplatePrint(T... args) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}


int main()
{
    map<string, int> arr={{"1", 1}, {"2", 2}, {"3", 3}};
    auto itr = arr.find("3");
    TemplatePrint<decltype(itr)>(itr);


    return 0;
}

GCC will print out real decltype of iterator (really real), which is :

void TemplatePrint(T ...) [with T = {std::_Rb_tree_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> >}]
benedict97
  • 25
  • 4