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
Can You Remove Elements from a Std::List While Iterating Through It
How to Determine If a String Is a Number With C++
Store Derived Class Objects in Base Class Variables
C++11 Return Value Optimization or Move
Floating Point Division VS Floating Point Multiplication
Difference Between an Int and a Long in C++
How to Call a Parent Class Function from Derived Class Function
Position of Least Significant Bit That Is Set
Operator Precedence VS Order of Evaluation
What Happens If I Assign a Negative Value to an Unsigned Variable
What Is the Purpose of the Most Vexing Parse
Adding External Library into Qt Creator Project
How to Use Cout ≪≪ Myclass
How to Safely Pass Objects, Especially Stl Objects, to and from a Dll
What Are Access Specifiers? Should I Inherit With Private, Protected or Public