9

Let's suppose to have a templateclass Foo:

template <typename T>
class Foo {
  void foo();
};

I have another template class Bar (independent from the first one):

template <int N>
class Bar {};

Let's say, I want to specialise the foo() method for whatever Bar class. I'd wrongly write:

template <>
template <int N>
void Foo<Bar<N> >::foo() { /* ... */ }

The compiler blames me for because the type is not complete:

error: invalid use of incomplete type 'class Foo<Bar<N> >'
 void Foo<Bar<N> >::foo() { }

Code

I am using C++98, but I'd like to know if there exist different solutions in C++11.


Note

I could solve the problem specialising the entire class Foo for a generic Bar, but after I should have to define all methods.

Example Code

That's not what I want, I am looking for (if exists) more elegant solution (both C++98 and C++11) which allows me to specialise and implement only a single class method.


EDIT:

The question on SO does not explain how to specialise with a template argument. Indeed, my question shows how the compiler complains about that.

BiagioF
  • 9,368
  • 2
  • 26
  • 50

4 Answers4

4

For C++11 you can SFINAE enable/disable (using std::enable_if) two differents versions of foo() inside a not specialized Foo class.

In C++98 you don't have std::enable_if but you can simulate it (give me some minutes and I try to propose an example). Sorry: my idea doesn't works because this method require the use of default template arguments for methods that is a C++11 innovation.

Another way is define a template base class for Foo(), say FooBase, insert foo() (and only foo()) in FooBase and specialize FooBase.

Another way, that works also with C++98, can be tag dispatching: you can define an unique foo(), with zero parameter, that call another foo(), with a parameter that is determined by T.

The following is a full (C++98 compilable) example

#include <iostream>

struct barWay   {};
struct noBarWay {};

template <int>
struct Bar
 { };

template <typename>
struct selectType
 { typedef noBarWay type; };

template <int N>
struct selectType< Bar<N> >
 { typedef barWay type; };

template <typename T>
struct Foo
 {
   void foo (noBarWay const &)
    { std::cout << "not Bar version" << std::endl; }

   void foo (barWay const &)
    { std::cout << "Bar version" << std::endl; }

   void foo ()
    { foo(typename selectType<T>::type()); }
 };


int main ()
 {
   Foo<int>        fi;
   Foo< Bar<42> >  fb;

   fi.foo();
   fb.foo(); 
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • Could always point them to http://en.cppreference.com/w/cpp/types/enable_if so they have visibility of usage and potential implementation. – Captain Obvlious Nov 07 '17 at 16:06
  • @CaptainObvlious - believe or not, I didn't think to see cppreference and I've implemented it ex novo (forgetting the `void` default value :( ). Anyway, my idea doesn't work because need a C++11 feature. – max66 Nov 07 '17 at 16:22
0

if a common base is not desirable, yet another way could be giving foo() a customization point, like a trait for example:

template <typename T>
struct foo_traits;

template <typename T>
struct Foo {
  void foo(){ foo_traits<T>::foo_cp(*this); }
};

template <typename T>
struct foo_traits{ static void foo_cp(T&){/*default*/} };

template <int N>
class Bar {};

template <int N>
struct foo_traits<Bar<N>>{ static void foo_cp(Foo<Bar<N>>&){/*spec*/} };

such trait could also be an implementation detail friend, if its only purpose is to internally provide a foo() specialization for Bar's.

Massimiliano Janes
  • 5,524
  • 1
  • 10
  • 22
0

If you cannot specialize foo, define it so that it delegates the call to an internal foo-implementation class. Then specialize that class.
Something like this should compile in C++98 and it doesn't differ much from your original code:

template <typename T>
class Foo {
    template<typename>
    struct FooImpl;

public:
    void foo() { FooImpl<T>()(); }
};

template <int N>
class Bar {};

template <typename T>
template <int N>
struct Foo<T>::FooImpl< Bar<N> > {
    void operator()() { /* ... */ }
};

int main() {
    Foo< Bar<0> > fb;
    fb.foo();

    Foo<int> fi;
    //fi.foo();
}

The last line doesn't compile as expected (at least I got it was the expected result, just define the function call operator for FooImpl otherwise).

This way you can define selectively the specializations for which you want foo to work. In all the other cases, an attempt at using foo will result in a compilation error.

skypjack
  • 49,335
  • 19
  • 95
  • 187
0

I'd like to know if there exist different solutions in C++11.

This is a classic use case for tagged dispatch, of which max66 already suggested. The approach, and even syntax, are basically the same in C++98 and C++11.

Here's a bit of a cleaner implementation than max66's, I believe (running on godbolt):

template <class T>
class Foo {
    template <class>
    struct tag{};
    template<class U>
    void foo_helper(tag<U>){std::cout << "default\n";}
    void foo_helper(tag<Bar<3> >){std::cout << "specialization for Bar<3>\n";}
public:
    void foo(){return foo_helper(tag<T>());}
};

The principle is the same; a client function accepting no arguments calls a helper function that constructs an empty type based on the T argument. Then normal overloading takes care of the rest.

Only here I use a templated catch-all method.


In C++11 the syntax would only change slightly; We could say tag<Bar<3>> instead of tag<Bar<3> > because new parsing rules allow the chevron for nested templates.

We could also make the tag and the templated foo_helper catch-all into variadic templates to be a little more generic:

template <class T>
class Foo {
    template <class...>
    struct tag{};
    template<class... U>
    void foo_helper(tag<U...>){std::cout << "default\n";}
    void foo_helper(tag<Bar<3>>){std::cout << "specialization for Bar<3>\n";}
public:
    void foo(){return foo_helper(tag<T>{});}
};

Things actually start getting pretty interesting in C++17 with the introduction of constexpr if that allows us to write what looks like normal branching logic based on T (Live Demo):

template <class T>
class Foo {
public:
    void foo(){
        if constexpr (std::is_same_v<T, Bar<3>>){std::cout << "Specialization for Bar<3>\n";}
        else std::cout << "default\n";
    }
};

As you can see, all the tag stuff goes away in favor of using a simple if statement.

We take advantage of type_traits introduced in C++11 to check the type of T against our desired type. Something like this wouldn't necessarily work previously because all branches needed to be compiled. In C++17, only the branch that is selected (at compile-time) is compiled.

Note that you could emulate this behavior as early as C++98 by using typeid (godbolt demo):

void foo(){
    if (typeid(T) == typeid(Bar<3>)){std::cout << "Specialization for Bar<3>\n";}
    else std::cout << "default\n";
}

However, the typeid approach is a poor choice for 2 reasons:

  1. It's a run time check (slow) for information we know at compile-time
  2. It's brittle because all branches must compile for all template instantiations, whereas in C++17 if constexpr only compiles the branch that is selected.
AndyG
  • 39,700
  • 8
  • 109
  • 143