J. D.
J. D.

Reputation: 279

Yet another class template specialisation (on a boolean parameter)

I am trying to specialise a very simple class templated on an integer, as follows.

#include <iostream>
#include <string>


constexpr bool is_even(int n) {
    if (n % 2 == 0)
        return true;
    else
        return false;
}


template<int N, bool b = is_even(N)>
class Number {
public:
  Number() {}
  void say() { std::cout << "I'm " + type << std::endl; }; 
private:
  std::string type;
};


// This seems to be the most natural way of doing the specialisation
template<int N>
Number<N, false>::Number() : type ("odd") {}


// Alternative specialisation I hoped would have worked
template<int N> class Number<N, true> {
public:
    Number() : type ("even") {}
};


int main() {
    Number<2> a;
    Number<3> b;

    a.say();
    b.say();
}

However, it seems I am doing something wrong in both partial specialisations. The compiler seems to be hinting I am declaring the false constructor inside the true constructor:

a.cc:26:26: error: invalid use of incomplete type ‘class Number<N, false>’
   26 | Number<N, false>::Number() : type ("odd") {}
      |                          ^
a.cc:15:7: note: declaration of ‘class Number<N, false>’
   15 | class Number {
      |       ^~~~~~
a.cc: In constructor ‘Number<N, true>::Number()’:
a.cc:32:13: error: class ‘Number<N, true>’ does not have any field named ‘type’
   32 |  Number() : type ("even") {}
      |             ^~~~
a.cc: In function ‘int main()’:
a.cc:40:4: error: ‘class Number<2>’ has no member named ‘say’
   40 |  a.say();

so maybe there's a syntax error somewhere? If so, the compiler is not helping in telling me where.

It seems as if the specialisation of my class does not know that we declared the string "type" as a private field in the general class.

Also, I noted that the compiler is not happy with a.say(), but seems to be fine with b.say().

Upvotes: 2

Views: 186

Answers (1)

Holt
Holt

Reputation: 37616

For this attempt:

template<int N>
Number<N, false>::Number() : type ("odd") {}

You cannot define a member function of a partially-specialized class if you have not partially specialized the whole class, i.e., declared the partial specialization before. See, e.g., Partial specialization of template class copy constructor.

For this attempt:

template<int N> class Number<N, true> {
public:
    Number() : type ("even") {}
};

You did a partial specialization of the whole class, which is perfectly valid, but doing so, you got a completely different class from the non-specialized version, thus the type member does not exists in this one.

What you would need is something like:

template<int N>
class Number<N, false> {
public:
  Number() : type("odd") {}

  // You need to re-declare everything...
  void say() { std::cout << "I'm " + type << std::endl; }; 
private:
  std::string type;
};

...in which cases you'd probably want to rethink your whole design, because re-declaring the whole class for both case is probably not what you want.

Upvotes: 1

Related Questions