0

I have used the std::enable_if metafunction in my class template to specify that it is only allowed to generate classes for variables that have GameCard as a base class. This works fine on its own when I implement the functions inline. However, if I want to implement the template functions outside the header body I run into the issue that I can't figure out how to correctly specify the function that I want to implement.

Cut down example code:

#include <string>
#include <memory>
#include <vector>

struct GameCard {};

template<class T, class = std::enable_if_t<std::is_base_of<GameCard, T>::value>>
struct CardStack {
  std::vector<T> cards;
  bool Test();
};

My IDE generates this as the function specification:

template<class T, class>
bool CardStack<T, <unnamed>>::Test() {
  return false;
}

This is obviously wrong since I'm getting compiler errors. But I don't have a clue on how to do it right. Do any of you know how to do this right?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
MaestroMaus
  • 342
  • 1
  • 6
  • 18

2 Answers2

3

Out of class definition should be:

template<class T, class Enabler>
bool CardStack<T, Enabler>::Test() {
  return false;
}

But currently, you class might be hijacked:

Whereas CardStack<int> or CardStack<int, void> won't compile thanks to SFINAE,

CardStack<int, char> would be "valid" (risk to not compile because of hard error produced by int in CardStack implementation).

static_assert seems enough in your case:

template<class T>
struct CardStack {
  static_assert(std::is_base_of<GameCard, T>::value);

  std::vector<T> cards;
  bool Test();
};

With simpler out of class definition:

template<class T>
bool CardStack<T>::Test() {
  return false;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Judging by your good answer (and Biagio's good answer) I am now sure that I am ignorant on this topic; because this was not how I perceived things to work. However, I find it really hard to find decent information with examples. Are there any sources you would recommend on this topic? – MaestroMaus Feb 14 '20 at 15:36
2

The function member definition should be something like:

template <class T, class U>
bool CardStack<T, U>::Test() {
    // body of function
}

Live example


The explanation is pretty straightforward, there is no back magic here. Just follow the normal syntactic rules of C++.

The definition of your class is a template class with two template parameters:

template<class, class>
struct CardStack { /* ... */ };

T is the name of the first one. On the other hand, the second does not have any type-name, but only a default type (= ...). Default type (similarly to default arguments for functions) does not have to be specified in the definition.

Therefore, the each method definition should be in the form:

template <class T, class U>
bool CardStack<T, U>::MethodName() {}
BiagioF
  • 9,368
  • 2
  • 26
  • 50