Class type non-type template parameter initialization does not compile
This template-argument
{.a=1, .b=2}
is not allowed according to the grammar for a template-argument which only allows the following constructs:
template-argument:
constant-expression
type-id
id-expression
A brace-init list is not any of the above constructs, it's actually an initializer and so it cannot be used as a template-argument.
You can be explicit about the type of the object that you use as the template-argument:
Bar<Foo{.a=1, .b=2}> bar;
and this will work, since this is a constant-expression.
Can a braced initializer be used for non-type template argument in C++?
I'd say GCC is wrong.
The grammar for template-argument in [temp.names] says that a template argument must either be a constant-expression, a type-id or an id-expression.
{}
is neither an expression, nor a type, nor an (un)qualified name.
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}>();
Will it be possible to omit the type name when initializing C++2a class non-type template arguments?
will C++2a allow the concrete type name S to be omitted?
No.
[temp.arg.nontype]/2
A template-argument for a non-type template-parameter shall be a converted constant expression (
[expr.const]
) of the type of the template-parameter.
In T<{0}>
, {0}
is not an S
: it is not an expression of the type of the template-parameter (S
). {0}
would be an initializer list (in a context where it would be allowed).
Bonus:
[dcl.init.list]/4
List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization.
No initialization occur for template-arguments (unless when it does, see [temp.arg.nontype]/1
).
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;
}
What does the C++20 standard say about usage of subojects as template non-type arguments?
The wording changed as part of P1907R1, which was adopted as part of C++20. Note that the first draft you cited - N4835 - predates this adoption (that draft was published Oct 2019, and this paper was adopted the following month at the Belfast meeting in Nov 2019). The closest draft to C++20 is N4861, which you can also conveniently view in html form.
As a result, the following:
template<int* p> class X {};
int a[10];
struct S
{
int m;
static int s;
} s;
X<&a[2]> x3;
X<&s.m> x4;
is a valid C++20 program, since neither a[2]
nor s.m
are subobjects of any of: a temporary, a string literal, the result of a typeid
expression, or __func__
.
The cppreference example has already been updated to reflect this, where the comments now read:
X<&a[2]> x3; // error (until C++20): address of array element
X<&s.m> x4; // error (until C++20): address of non-static member
Can I use a type parameter in a template for a non-type parameter?
If you are OK to have a fixed default per type you can create a type to define it as a constant and specialise it as needed.
template <typename T> struct MyTypeDefault { static const T value; };
template <typename T> const T MyTypeDefault<T>::value = T();
template <> struct MyTypeDefault<double> { static const double value; };
const double MyTypeDefault<double>::value = 1.0;
template <typename T>
class MyType {
public:
MyType() { value = MyTypeDefault<T>::value; }
private:
T value;
};
Related Topics
At^Sysinfo and a C++ Terminal Program
Polymorphic_Allocator: When and Why Should I Use It
Cleaning Up an Stl List/Vector of Pointers
Get the Status of a Std::Future
"If" Argument Evaluation Order
Why Do We Need a Virtual Table
Deleting Specific Line from File
What Is the Best Modern C++ Approach to Construct and Manipulate a 2D Array
Is a Destructor Called When an Object Goes Out of Scope
Scale and Rotation Template Matching
How to Extract the Source Filename Without Path and Suffix at Compile Time
Why Is Partial Specialization of a Nested Class Template Allowed, While Complete Isn'T