Non-Type Template Parameters

Non-type template parameters

The reason you can't do this is because non-constant expressions can't be parsed and substituted during compile-time. They could change during runtime, which would require the generation of a new template during runtime, which isn't possible because templates are a compile-time concept.

Here's what the standard allows for non-type template parameters (14.1 [temp.param] p4):

A non-type template-parameter shall have one of the following (optionally cv-qualified) types:

  • integral or enumeration type,
  • pointer to object or pointer to function,
  • lvalue reference to object or lvalue reference to function,
  • pointer to member,
  • std::nullptr_t.

Why is this code complaining about a non-type template parameter cannot have type?

You are trying to parametrize template with std::greater<T> which is not an acceptable type for template parameter (cppreference):

  • std::nullptr_t (since C++11)
  • integral type (note: bool is 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.

Non-type template parameters and requires

If you only have a boolean condition and nothing else, do this:

template<Bla b>
requires(b > 1)
void f() {}

Alternative longer syntax, if you need to check more things in the same requires-expression:

template<Bla b>
requires requires
{
requires b > 1;
// ^~~~~~~~
}
void f() {}

C++20 non-type template parameter which is template in the prior type parameters: is not a valid template arg, because is not a variable

Edit:

The third template parameter of sillier is a non-type template argument, which can only be bound to a variable, but dumb_func is not a variable.

This explanation doesn't make sense, and in fact the code is probably fine, and the error might just be a bug as discovered and reported in this answer. The fix given below still works though.


You can make the third template parameter be a non-type template parameter of reference type.

template<typename T, typename J, silly<T, J> const & aSilly>
// ^^^^^^^
struct sillier
{
const uint32_t something;
};

Also, the member variable f in silly is declared as a function pointer returning a const void type:

const void (*f)(T,J);

So you either need to remove the const from the return type of f, or you can change the declaration of dumb_func to return a const void type:

const void dumb_func(uint32_t i, uint32_t j)
{
return;
}

There doesn't seem to be any point in a const void returning function, so I would go with the first option.


Here's a demo.

template non-type template parameters

Your base declaration does not match your specialization.

The base implementation has template <class...Args> while the specialzation wants template <int eventType, class...Args>.

You also put an extra int that does not belong there in the declaration for the specialization here:

template<int eventType,class...Args>
class Event<int eventType, bool(Args...)> : public IEvent
^^^ here

The adjusted code would look like this

#include <stdio.h>
#include <iostream>
#include <functional>
#include <vector>
using namespace std;

class IEvent
{

public:
int m_EventType;
virtual ~IEvent() {}
};

template<int eventType, class...Args>
class Event : public IEvent {};

template<int eventType,class...Args>
class Event<eventType, bool(Args...)> : public IEvent
{
public:
Event(bool(*func)(Args...)) :m_FnPtr(func)
{
m_EventType = eventType;
m_ListenersList.push_back(m_FnPtr);
}

template <typename T>
Event(T* obj, bool(T::* Func)(Args...))
{
m_EventType = eventType;
m_FnPtr = [obj, Func](Args&&... args)->bool {
return (obj->*Func)(std::forward<Args>(args)...);
};

m_ListenersList.push_back(m_FnPtr);
}

void NotifyListeners(Args&&...args) const
{
for (auto& itr : m_ListenersList)
{
(itr)(std::forward<Args>(args)...);
}

}
private:
std::function<bool(Args...)> m_FnPtr;
std::vector<std::function<bool(Args...)>> m_ListenersList;
};

class Window
{
public:
bool OnKeyUp(bool, double)
{
cout << endl << "Member Function called";
return true;
}

bool OnClicked()
{
cout << endl << "OnClicked";
return true;
}

//using KeyupListenerType = Event<"KeyUp", bool(bool, double)>;
};

int main()
{
Window w;
Event<90,bool(bool, double)> evnt(&w, &Window::OnKeyUp);
//Event<100,bool()> evnt(&w, &Window::OnClicked);
evnt.NotifyListeners(true, 6.8);
return 0;
}

a non-type template parameter cannot have type

Clang just doesn't implement class types as non-type template parameters yet, see P1907 in this table.

gcc does implement them but there's actually an issue here. The grammar for template-argument doesn't actually allow for a braced-init-list. This is a clear language defect (there was never a reason to have such a thing before P1907 but now there's certainly no reason to not have it). This is a language bug at the moment. Nevertheless, gcc went ahead and does support a braced-init-list as a template argument... just not a designated-initializer-list.

So that blog post of mine is running ahead of the actual language by a bit... Until the language catches up, even though this is technically unsupported:

takes_tmpl_point<{.x=1, .y=2}>(); 

This is definitely valid:

takes_tmpl_point<Point{.x=1, .y=2}>();

template non-type template parameter

You cannot directly reference arguments to template template parameters. They are not in scope. But you could do something like this:

#include <utility>

template <class>
class pow10_helper {};

template <template <typename T, T ...> class IntSeq, class T, T ... Ints>
struct pow10_helper<IntSeq<T, Ints...>> {
static constexpr long value = ((Ints, 10) * ...);
};

template <size_t pow>
static constexpr long pow10 = pow10_helper<std::make_index_sequence<pow>>::value;
#include <iostream>
int main() {
size_t pow = pow10<3>;
std::cout << pow << std::endl; // prints 1000
}

That being said, there are simpler ways of implementing pow10:

template <size_t pow>
struct pow10 { static constexpr size_t value = 10 * pow10<pow-1>::value; };
template <> struct pow10<0> { static constexpr size_t value = 1; };
int main() {
size_t pow = pow10<3>::value;
std::cout << pow << std::endl; //prints 1000
}


Related Topics



Leave a reply



Submit