3

I have some very simple code, which looks like so:

template <typename T, const T DEFAULT>
class One
{
    T *p;
};

template <typename T, const T DEFAULT>
class Two
{
    One<One<T, DEFAULT>, DEFAULT> *p;
};

When I try to compile it, I get an error message:

error: 'class One' is not a valid type for a template non-type parameter

When, however, I change const T DEFAULT to typename T2 and DEFAULT to T2, it starts working:

template <typename T, typename T2>
class One
{
    T *p;
};

template <typename T, typename T2>
class Two
{
    One<One<T, T2>, T2> *p;
};

But, it is not what I want. I need my first variant of code work, but I do not know what is wrong with that and how can I fix it.

Jacobian
  • 10,122
  • 29
  • 128
  • 221
  • Only primitive types like `int` or `bool` can be used as non type template parameters. Provide a specialization for `One` instead. –  Feb 04 '18 at 17:10
  • Possible dupe: https://stackoverflow.com/questions/5687540/non-type-template-parameters – Rakete1111 Feb 04 '18 at 17:10
  • Well, as the compiler told you, you can't use type `One` for non-type template parameter. Your second example is completely different since it uses type parameter instead of non-type parameter. – AnT stands with Russia Feb 04 '18 at 17:12
  • Beyond `One` not being a valid type for a non-type template parameter, there's also the issue of `DEFAULT` *not being* of type `One` (as `One` requires). This smells like an X/Y problem. – StoryTeller - Unslander Monica Feb 04 '18 at 17:17

2 Answers2

3
template <typename T, const T DEFAULT>
class One
{
    T *p;
};

template <typename T, const T DEFAULT>
class Two
{
    One<One<T, DEFAULT>, DEFAULT> *p;
};

There are a couple problems with this:

  1. DEFAULT isn't the same type as One<T, DEFAULT>; DEFAULT is of type T. Thus you can't use One<One<T, DEFAULT>, DEFAULT>.

  2. Only a few types are valid as template non-type parameters. Quoting cppreference:

    • std::nullptr_t (since C++11);
    • integral type;
    • lvalue reference type (to object or to function);
    • pointer type (to object or to function);
    • pointer to member type (to member object or to member function);
    • enumeration type.

One<T, DEFAULT> is not one of the above, so it can't be used as a template non-type parameter

Justin
  • 24,288
  • 12
  • 92
  • 142
  • And If T in my case is supposed to be of integer type, can I somehow provide some default specialization, like @TheDude suggests? – Jacobian Feb 04 '18 at 17:16
  • @Jacobian If `T` is supposed to be an integer type, yes you can do that. You just need to ensure that the non-type parameter is of an integer type. You may be interested in [`std::integral_constant`](http://en.cppreference.com/w/cpp/types/integral_constant) – Justin Feb 04 '18 at 17:18
  • @Justin Unfortunately the cppreference doesn't state that `double` or `float` types are also not allowed, or don't these count as _integral types_? –  Feb 04 '18 at 17:18
  • @TheDude ["Types bool, char, char16_­t, char32_­t, wchar_­t, and the signed and unsigned integer types are collectively called integral types."](http://eel.is/c++draft/basic.types#basic.fundamental-7.sentence-1) – Rakete1111 Feb 04 '18 at 17:19
  • @TheDude `double` and `float` aren't [integral types](http://en.cppreference.com/w/cpp/types/is_integral). They are [arithmetic types](http://en.cppreference.com/w/cpp/types/is_arithmetic) – Justin Feb 04 '18 at 17:21
  • @Rakete1111 Thanks for the reference. Good to know. –  Feb 04 '18 at 17:21
  • @Justin. "If T is supposed to be an integer type, yes you can do that.". Would you be so kind to elaborate on this a little bit in your answer and provide some tiny example to show how it works? – Jacobian Feb 04 '18 at 17:21
  • @Jacobian IMO, that's a different question. It's also answered elsewhere on the site. But the easiest thing to do might be a `static_assert(std::is_integral::value, "");` in the body of `One`. – Justin Feb 04 '18 at 17:27
0
template <typename T, const T DEFAULT>
class One

The second T refers to the first one and is the type of the non-type template parameter called DEFAULT.

For example, you could instantiate the One template like this:

One<int, 12345> // T is int, DEFAULT is 123456

Or like this:

One<char, 100> // T is char, DEFAULT is 100

Or like this:

One<int*, nullptr> // T is int*, DEFAULT is nullptr

Those examples have in common that T must be a valid non-type template argument. You could not, for example, use a type template argument for it:

One<std::string, 123> // fails, because `std::string` cannot be used as `DEFAULT`

Now if you look at your second class...

template <typename T, const T DEFAULT>
class Two
{
    One<One<T, DEFAULT>, DEFAULT> *p;
};

In the attempted outer instantiation of One, T would be One<T, DEFAULT>. And that's exactly like the std::string example above; One<T, DEFAULT> isn't a non-type template argument.

There is no "fix" for this, because the concept of your Two class is fundamentally broken.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62