Template specialization: ‘Scalar’ does not name a type

1.5k views Asked by At

I'm trying to implement a template specialization for complex scalars and, with the help of Stackoverflow, got to using std::enable_if_t and its poor man's version

#include <type_traits>
#include <complex>

// declarations
namespace Test {
  template<class Scalar>
    class A {
      public:
        A(const Scalar z);
        Scalar realPart();

      private:
        Scalar z_;
    };
}

// definitions
namespace Test {
  template<bool B, class T = void>
    using enable_if_t = typename std::enable_if<B,T>::type;
  template<class T> struct is_complex : std::false_type {};
  template<class T> struct is_complex<std::complex<T>> : std::true_type {};

  template<class Scalar>
    A<Scalar>::
    A(const Scalar z) : z_(z)
  { }

  template<class S = Scalar, enable_if_t<is_complex<S>{}>* = nullptr>
    Scalar
    A<Scalar>::realPart()
    {
      return z_.real();
    }

  template<class S = Scalar, enable_if_t<!is_complex<S>{}>* = nullptr>
    Scalar
    A<Scalar>::realPart()
    {
      return z_;
    }
}

int main() {
}

for C++11. The above code, separating declarations and definitions, however, fails to compile with

test4.cpp:29:22: error: ‘Scalar’ does not name a type
   template<class S = Scalar, enable_if_t<is_complex<S>{}>* = nullptr>
                      ^

It's not clear to me how this fails. Any hints?

2

There are 2 answers

1
Barry On BEST ANSWER

In this code:

template<class S = Scalar, enable_if_t<is_complex<S>{}>* = nullptr>
Scalar A<Scalar>::realPart()
{
  return z_.real();
}

Scalar doesn't name a type because it isn't one. That's just the name of a template parameter that you've been using. What you had intended to write was:

template<class Scalar, enable_if_t<is_complex<Scalar>{}>* = nullptr>
Scalar A<Scalar>::realPart()
{
  return z_.real();
}

However, this won't work either, since A doesn't have a second template non-type argument and you're trying to pass it one. What you're really trying to do is partially specialize the member function A<Scalar>::realPart() and that's impossible in the language.

What you need to do instead is dispatch to a helper that will know what to do. Something like:

template <class Scalar>
Scalar A<Scalar>::realPart() {
    return getRealPart(z_);
}

with:

template <typename Scalar>
Scalar getRealPart(Scalar r) { return r; }

template <typename Scalar>
Scalar getRealPart(std::complex<Scalar> c) { return c.real(); }

With a more complicated type trait, we'd do something like:

template <class Scalar>
Scalar A<Scalar>::realPart() {
    return getRealPart(z_, is_complex<Scalar>{});
}

And have overloads taking true_type and false_type as the second argument. In this particular case, that's unnecessary.

0
utnapistim On

I don't have a compiler at hand, but I think this:

template<class S = Scalar, enable_if_t<is_complex<S>{}>* = nullptr>
A<Scalar>::
Scalar realPart()
{
  return z_.real();
}

should be:

template<typename Scalar, enable_if_t<is_complex<Scalar>{}>* = nullptr>
//       ^^^^^^^^^^^^^^^                         ^^^^^^
typename Scalar A<Scalar>::realPart()
//^^^^^^^^^^^^^^^^^^^^^^^^^
{
  return z_.real();
}

... and the same for the other definition.