In c++ std library, is_member_pointer is implemented as
template<typename _Tp>
struct __is_member_pointer_helper
: public false_type { };
template<typename _Tp, typename _Cp>
struct __is_member_pointer_helper<_Tp _Cp::*>
: public true_type { };
/// is_member_pointer
template<typename _Tp>
struct is_member_pointer
: public __is_member_pointer_helper<typename remove_cv<_Tp>::type>::type
{ };
Can someone explain that how the _Cp is deduced? It works like a magic.
The type of a pointer-to-member is Type Class::*, where Type is the object type or function type being pointed to. If you provide the template an int C::*, for example, the compiler can simply deduce the class type by examining the type of the pointer-to-member and seeing the class type is C. It would also deduce the pointed-to type as int the same way. It works very similarly to how we humans would do it. In general, we call this technique pattern matching, which you might be familiar with from regular expressions.
Formally:
[temp.deduct.type]/3.2:
A pointer-to-member type includes the type of the class object pointed to and the type of the member pointed to.
[temp.deduct.type]/8:
A template type argument T, a template template argument TT or a template non-type argument i can be deduced if P and A have one of the following forms:
[snip]
T T::*
[snip]
Where per [temp.deduct.type]/1:
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
Some years ago I basically had the same misunderstanding about C++ template specialization that, apparently, you are having now.
The other answers are great, but I don't think they would have really helped me back then understanding what's going on. So, assuming that you are suffering from the same misunderstanding that I did, let me try to explain how I finally got my thoughts right:
Back then, my intuitive understanding erroneously told me that the term "specialization" means that the "specialized template" should somehow have less template arguments than the "original template". This assumption was driven by the fact that almost every tutorial code that tries to explain how specialization works starts with an example like
template <class T> // <-- one parameter
class MyClass { ... };
template <> // <-- zero parameters
class MyClass<int> { ... };
Now, your example of is_member_pointer is one counterexample showing that this is not true at all.
My entire misunderstanding started with using wrong terminology. You may have noticed that above I put quotes around "specialized template" and "original template"? I did this because it's wrong, but those were the words that I was using back then.
True is, that there is only one template. It's wrong to say that there are two templates, an original one and a specialized one. In my example
template <class T>
class MyClass { ... };
is the template, while
template <>
class MyClass<int> { ... };
is a specialization of that same template.
What makes it a specialization, is the use of <int> behind the class name. That's it!
This is another valid specialization of that same template:
template <class... Types>
struct many_to_one { ... };
template <class A, class B, class C, class D> // <-- four parameters, could be even more
class MyClass<many_to_one<A, B, C, D>> { ... };
As you can see, the specialization has way more template parameters than the actual template. And that's perfectly valid as long as the number of specialization types (one type in this example, namely many_to_one<A, B, C, D>) matches the number of template parameters of the actual template.
Now, what does the compiler do, if you use MyClass<int> anywhere, e.g. for declaring a variable of that type?
It has a look at the template and all specializations of it.
First thing to note: As in this example there is only one template parameter, something like MyClass<int, double, short, float> cannot compile, even though there is a specialization with four parameters! But those four parameters apply to the specialization, not to the template.
When the compiler walks through all the specializations and it finds
template <class A, class B, class C, class D>
class MyClass<many_to_one<A, B, C, D>> { ... };
it has to ask itself "are there any types A, B, C, D so that the given type (int) is equal to the specialization type many_to_one<A, B, C, D>? The answer is No, so it moves on to the next specialization:
template <>
class MyClass<int> { ... };
Again, it asks itself "are there any types <empty list here> so that the given type (int) is equal to the specialization type int? Obviously, yes! Since there are no even better matching specializations, it chooses this one.
If instead you have e.g. MyClass<double>, both questions for the two specializations yield No as the answer, so the "basic" template will be chosen.
So, to finally answer your original question: If you write e.g.
std::is_member_pointer<decltype(&std::string::size)>
the compiler looks at the specialization and sees "Oh, look. If I put _Tp = size_t and _Cp = std::string then the given type decltype(&std::string::size) is equal to the specialization type _Tp _Cp::*, so I pick that specialization (that happens to inherit from std::true_type)".
But if you write e.g.
std::is_member_pointer<int>
then it cannot find any types for _Tp and _Cp to make _Tp _Cp::* equal to int, so it discards that specialization and chooses the "basic" template (that happens to inherit from std::false_type).
There is nothing magical about it. It is simple specialization, and _Cp is deduced to containing class, whenever the template is instantiated for the class member.
It is an application of general case of selecting best available specialization.
Related
template<template<auto> class> struct A {};
template<int&> struct B {};
A<B> a;
int main() {}
All three compilers MSVC, GCC and Clang in their latest versions accept this code (https://godbolt.org/z/b7Pv7Ybxv). However, auto cannot deduce to a reference type, so why would this be allowed? Or are the compilers incorrectly accepting it when looking at what the standard says?
I confused the argument and parameter in the above. My intention was to ask about
template<template<int&> class> struct A {};
template<auto> struct B {};
A<B> a;
int main() {}
which is also accepted by all of the compilers above. The original example is however also interesting because the template template parameter should be at least as specialized as the template argument, which it doesn't intuitively seem to be.
The second example is OK according to the rules. Let us quote them:
#3 A template-argument matches a template template-parameter P when P is at least as specialized as the template-argument A ...
#4A template template-parameter P is at least as specialized as a template template-argument A if, given the following rewrite to two function templates, the function template corresponding to P is at least as specialized as the function template corresponding to A according to the partial ordering rules for function templates. Given an invented class template X with the template-head of A (including default arguments and requires-clause, if any):
Each of the two function templates has the same template parameters and requires-clause (if any), respectively, as P or A.
Each function template has a single function parameter whose type is a specialization of X with template arguments corresponding to the template parameters from the respective function template where, for each template parameter PP in the template-head of the function template, a corresponding template argument AA is formed. If PP declares a template parameter pack, then AA is the pack expansion PP... ([temp.variadic]); otherwise, AA is the id-expression PP.
So our template-argument is template<auto> class and our template parameter is template<int&> class. Let's invent a class template X:
template <auto> class X {};
and rewrite as two function templates:
// rewritten argument
template<auto V> void func(X<V>);
// rewritten parameter
template<int& V> void func(X<V>);
The second one seems to be at least as specialized as the first: every function call that would be satisfied by the second template would be also satisfied by the first one. (As far as I can tell the second template can never be satisfied because you cannot instantiate X with an int& argument; not sure if this is IFNDR; probably not because the program per se does not contain func templates, they are invented in order to check other templates).
Less obviously, the first example is also OK according to the same rules. Lety's invent class template X and rewrite again:
template <int&> class X {};
// rewritten argument
template<int& V> void func(X<V>);
// rewritten parameter
template<auto V> void func(X<V>);
Now the two templates satisfy exactly the same calls, so each one is at least specialized as the other one.
I've got the following code:
template <bool condition>
struct enable_if { };
template <>
struct enable_if<true> { using type = bool; };
template <typename T>
class is_callable {
using Yes = char[1];
using No = char[2];
template <typename U> static Yes& filter(decltype(&U::operator()));
template <typename U> static No& filter(...);
public:
constexpr operator bool() { return sizeof(filter<T>(nullptr)) == sizeof(Yes); }
};
template <typename Lambda, typename enable_if<is_callable<Lambda>{}>::type = true>
void doSomethingWithLambda(Lambda func) {
func();
}
int main() {
doSomethingWithLambda([]() { });
}
The important part is the enable_if<is_callable<Lambda>{}>::type part.
One is forced to instantiate is_callable<Lambda> with {} because if one were to use (), C++ would mistake it for a function call.
Feel free to correct me if I'm wrong, but as far as I know, C++ assumes it is a function in the () case so that the type of expression isn't determined after the time of writing, saving everyone a headache. What I mean by that is, assuming you had a function version and a class version of is_callable (separated by SFINAE using enable_if or something along those lines), the type Lambda could determine the true meaning of (), either a function call or an instantiation. Like I said, as far as I know, C++ wants to avoid this confusion, so it assumes function call and fails if such a function does not exist.
Based on the assumptions above, the following shouldn't work:
enable_if<(bool)is_callable<Lambda>()>::type
What does it matter if I cast the result of the function call (never mind that functions couldn't even be evaluated in this context)? Why is this suddenly treated as an instantiation instead of a function call?
No, your understanding is not correct.
Firstly, a name can't refer to both a class template and a function template. If that happens the program is ill-formed. (And defining both in the same scope is not allowed to begin with.)
Secondly, is_callable<Lambda>() as template argument is not a function call to begin with. It is a function type. It is the type of a function which has no parameters and returns a is_callable<Lambda>.
When the compiler parses a template argument, it can interpret it in two ways: Either as a type or as an expression (or as a braced-init-list), because template parameters can be type parameters or non-type parameters.
When the compiler reads is_callable<Lambda>() it notices that is_callable is a class template and then realizes that is_callable<Lambda> is therefore a type. If you have a type, let's shorten it to T, then T() can either be syntax representing the type of a function returning T and taking no arguments, or it can be an expression formed from one single functional notation explicit cast (which you imprecisely call "instantiation").
There is no way to differentiate these two cases in the context, but the compiler needs to know whether this is a type template argument or a non-type template argument. So there is a rule saying that such ambiguities are always resolved in favor of a type.
If is_callable was a function template instead, there would be no ambiguity, because then is_callable<Lambda> is not a type and therefore is_callable<Lambda>() cannot be a function type. It must be a function call instead and therefore an expression and non-type template argument.
When you write (bool)is_callable<Lambda>() this is not valid syntax for a type and therefore there is no ambiguity. It is a non-type template argument and an expression. And is_callable<Lambda>() is a funcational notation explicit cast because is_callable<Lambbda> is a type. If is_callable was a function template instead of a class template, then it would be a function call.
In my quest to better understand templates and meta-programming in C++ I'm reading this article, but my understanding of the code snippets quickly diminishes, e.g.:
template<class A, template<class...> class B> struct mp_rename_impl;
template<template<class...> class A, class... T, template<class...> class B>
struct mp_rename_impl<A<T...>, B>
{
using type = B<T...>;
};
template<class A, template<class...> class B>
using mp_rename = typename mp_rename_impl<A, B>::type;
The code is used like:
mp_rename<std::pair<int, float>, std::tuple> // -> std::tuple<int, float>
mp_rename<mp_list<int, float>, std::pair> // -> std::pair<int, float>
mp_rename<std::shared_ptr<int>, std::unique_ptr> // -> std::unique_ptr<int>
Can someone please explain the code like I'm five?
I do have a general and basic understanding of non-templated C++.
What I don't get is:
Why is mp_rename_impl forward declared with two type parameters (class A, template<class...> class B), then it's defined and specialized at the same time[*] with three (template<class...> class A, class... T, template<class...> class B) and respectively two(A<T...>, B) type parameters?
I understand that it aliases (using type = B<T...>;) the type to be B<T...> instead of A<T...>, but I don't really get how it is done.
Also why is A a template template parameter only in the specialization?
[*] most certainly I got something wrong here
Why is mp_rename_impl forward declared with two type parameters (class A, template<class...> class B), then it's defined and specialized at the same time[*] with three (template<class...> class A, class... T, template<class...> class B) and respectively two(A<T...>, B) type parameters?
The forward declaration establishes the number of parameters needed to instantiate mp_rename_impl, and that the former should be an actual type, the latter a template.
Then when there's an actual instantiation, it tries to match the specialisation struct mp_rename_impl<A<T...>, B>, and in doing so it can consider any combination of values for the specialisation's A, T... and B matching the specialisation's expectations: namely template<class...> class A, class... T, template<class...> class B. Note that the A parameter in the specialisation shares a name with the A in the declaration, but is not the same - the former being a template and the latter a type. Effectively, to match the specialisation a template instantiation must have been passed as the declaration's A parameter, and that template's parameters are captured at T.... It imposes no new restrictions on what can be passed as B (though the using statement does - B<T...> needs to be valid or you'll get a compilation error - too late for SFINAE to kick in).
Also why is A a template template parameter only in the specialization?
The specialisation calls that parameter A, but it's not conceptually the same as A in the declaration. Rather, the former's A<T...> corresponds to the latter A. Perhaps the specialisation should have called it "TA" or something else to indicate it's the template from which an actual A can be formed in combination with the T... parameters.
The specialisation is then of A<T...>, B, so the compiler works backwards from whatever instantiation is actually attempted to find valid substitutions for A, T... and B, guided by the restrictions on their form specified in template<template<class...> class A, class... T, template<class...> class B>.
What this achieves it to make sure the specialisation is only matched when the two parameters are a template already given some set of parameters types, and a template able to take a list of parameter types. The matching process effectively isolates the T type list so it can be reused with B.
My first attempt wasn’t what you were looking for, so let me briefly try to go back and explain like you’re six.
It’s not forward-declared in the sense that a function has a prototype and a definition. There’s an implementation for any A, and that compiles to an empty struct (which is a unique type to the compiler, but doesn’t require any actual storage or run-time code). Then, there’s a second implementation, only for template classes A.
There are really two templates in the second definition. What’s going on is that the second definition it takes the two parameters A and ... T and turns them into a type A<T>, which becomes the first parameter of mp_rename_impl<A<T...>,B>. So it applies to any A that’s a template class. But that’s a more specific kind of A! So it’s a specialization that needs to declare a struct with a type definition in its scope. Finally, the third variant is not a specialization of the template at all. It’s declaring the templated mp_rename as an alias for the more complicated type stored within the scope of every struct in the second declaration, which as you see is the identifier type in the scope mp_rename_impl<A, B>. Believe it or not, this makes his template code a lot more readable.
Why does the more generic definition at the top expand to just an empty struct? When A is not a template class, the contents are trivial, but it does need some kind of type to its name so the compiler will consider it distinct from every other type. (It would have been more cool to write my example below to generate classes with the static constants as members, rather than functions. In fact, I just did.)
Updated as threatened to make my templates a bit more like his:
Okay, template metaprogramming is a kind of programming where, instead of having the program compute something when it runs, the compiler computes it ahead of time and stores the answer in the program. It does this by compiling templates. This can be a lot faster to run, sometimes! But there are limits on what you can do. Mainly, you can’t modify any of your parameters, and you’ve got to be sure the computation stops.
If you’re thinking, “You mean, just like functional programming?” you are one very smart five-year-old. What you normally end up doing is writing recursive templates with base cases that expand either to unrolled, streamlined code, or to constants. Here’s an example that might seem familiar from your Intro to Computer Science class when you were three or maybe four:
#include <iostream>
using std::cout;
using std::endl;
/* The recursive template to compute the ith fibonacci number.
*/
template < class T, unsigned i >
struct tmp_fibo {
static const T fibo = tmp_fibo<T,i-1>::fibo + tmp_fibo<T,i-2>::fibo;
};
/* The base cases for i = 1 and i = 0. Partial struct specialization
* is allowed.
*/
template < class T >
struct tmp_fibo<T,1U> {
static const T fibo = (T)1;
};
template < class T >
struct tmp_fibo<T,0U> {
static const T fibo = (T)0;
};
int main(void) {
cout << "fibo(50) = " << tmp_fibo<unsigned long long, 50>::fibo
<< ". fibo(10) = " << tmp_fibo<int, 10>::fibo << "."
<< endl;
return 0;
}
Compile to assembly language, and we see what code the compiler generated for the line tmp_fibo<unsigned long long, 50>::fibo Here it is, in full:
movabsq $12586269025, %rsi
The templates generates an integral constant within each structure at compile-time. What those examples are doing, since you can declare type names within structs, is doing the same thing for types.
I will try to make it simple. Template metaprogramming is about computing types at compile-time (you can also compute values, but just let us focus on that).
So if you have this function:
int f(int a, int b);
You have a function that returns an int value given two int values.
You use it like this:
int val = f(5, 8);
Metafunctions operate on types, instead of on values. A metafunction looks like this:
//The template parameters of the metafunction are the
//equivalent of the parameters of the function
template <class T, class U>
struct meta_f {
typedef /*something here*/ type;
};
Namely, a metafunction has a nested type inside, by convention the nested type is called type.
So you invoke a metafunction like this in non-generic contexts:
using my_new_type = meta_f<int, float>::type;
In generic contexts you must use typename:
using my_new_type = typename meta_f<T, U>::type;
This returns a type, not a run-time value, since we said metafunctions operate on types.
Examples of metafunctions in the standard library can be found in header
type_traits, among others. You have add_pointer<T> or decay<T>. These metafunctions return new types given a type.
In C++14, in order to avoid pieces of code like this, which are verbose:
using my_computed_type = typename std::add_pointer<T>::type;
Some template aliases with a _t suffix, again by convention, were created that invoke the metafunction, directly, for you:
template <class T>
using add_pointer_t = typename std::add_pointer<T>::type;
And now you can write:
using my_computed_type = std::add_pointer_t<T>;
All in all, in a function, you have runtime values as parameters, in a metafunction, the parameters are types. In a function you invoke with the
usual syntax and obtain a runtime value. In a metafunction you get the ::type nested type and get a new computed type.
//Function invocation, a, b, c are values of type A, B, C
auto ret = f(a, b, c);
//Meta function invocation. A, B, C are types
using ret_t = typename meta_f<A, B, C>::type;
//Typical shortcut, equivalent to metafunction invocation.
using ret_t = meta_f_t<A,B,C>;
So for the first function you get a value, for the others, you get a type, not a value.
template <typename T, typename = void>
struct IsIterable : std::false_type {};
template <typename T>
struct IsIterable<
T,
decltype(
std::begin(std::declval<std::add_lvalue_reference_t<T>>()),
std::end(std::declval<std::add_lvalue_reference_t<T>>()),
void()
)
> : std::true_type {};
I thought I understood this.
The first IsIterable is a class template with a default template argument for the second parameter. It obviously is applicable for every type.
The second part is a partial template specialization of IsIterable. Thanks to SFINAE, it is only chosen when T has the member functions begin() and end().
Here is my first question I just came up with actually: the base template has a default argument, so this means it still has two template parameters. The partial specialization only has one template parameter. So does this mean that "one template parameter" always gets choosen before "two template parameters, one default"?
Also, do I understand it correctly that the first "base" template inherits from false_type, and the partial template specialization "adds" another inheritance level? (so does the partial specializations inheritance hierarchy look like this: false_type > true_type > IsIterable, where the definitions of false_type are hidden by true_type?)
Now onto my actual question. Why does the decltype expression have to evaluate to void? I thought that it wouldn't matter and I could write
template <typename T>
struct IsIterable<
T,
decltype(
std::begin(std::declval<std::add_lvalue_reference_t<T>>()),
std::end(std::declval<std::add_lvalue_reference_t<T>>()),
bool() // **** change here ****
)
> : std::true_type {};
as well. But this makes it so that the value of IsIterable is always false! Why is, when I change the partial specialization from void to bool, always the first template chosen?
So does this mean that "one template parameter" always gets choosen before "two template parameters, one default"?
No. A partial specialization is used when an instantiation matches its template argument list, and when it is "more specialized" than any other partial specializations that match (which is not relevant here, because there are no other partial specializations declared).
When you instantiate the template as IsIterable<Foo> the default template argument is used, so the instantiation is IsIterable<Foo, void>. If Foo has begin() and end() members then IsIterable<Foo, void> matches the partial specialization, which is more specialized than the primary template. When Foo doesn't have begin() and end() members the partial specialization is not usable, because the expression decltype(std::declval<T>().begin(), std::declval<T>().end(), void()) causes a subsitution failure when Foo is substituted in place of T.
Also, do I understand it correctly that the first "base" template inherits from false_type, and the partial template specialization "adds" another inheritance level?
No. There is no implicit inheritance relationship between a primary template and a specialization. (If you stop calling it a "base" template and call it by its proper name, the primary template, maybe you won't think there is any kind of "base" class relationship.)
When a partial specialization (or an explicit specialization) is used it is used instead of the primary template, not in addition to it.
Why does the decltype expression have to evaluate to void?
Because that's the type of the default template argument on the primary template. You are not supposed to provide an argument for the second template parameter, it's supposed to use the default, and that is void.
Why is, when I change the partial specialization from void to bool, always the first template chosen?
Because when you write IsIterable<Foo> that uses the default template argument, so is equivalent to IsIterable<Foo, void> which can never match a partial specialization of the form IsIterable<T, bool> because void is not the same type as bool!
If you made the partial specialization use bool() instead then you would have to write IsIterable<Foo, bool> for the partial specialization to match. You could do that ... but it's not how the trait is designed to be used. Alternatively you could change the default template argument on the primary template to be bool as well, but void is the idiomatic choice, since the specific type doesn't matter, all that matters is the default matches the specialization. You're meant to provide only one template argument and let the default be used for the second. And the partial specialization can only match if its second template argument is the same as the default template argument.
The second part is a partial template specialization of IsIterable. Thanks to SFINAE, it is only chosen when T has the member functions begin() and end().
That's not all there is. The expression t.begin(), t.end(), void() for a t of type T must also have a valid type and that must be void for the specialization to be selected. (That's what the decltype achieves.)
Here is my first question I just came up with actually: the base template has a default argument, so this means it still has two template parameters. The partial specialization only has one template parameter. So does this mean that “one template parameter” always gets choosen before “two template parameters, one default”?
Every valid instantiation of the specialization is also a valid instantiation of the base case (it's an implication) so the specialization (if viable) is a better match.
Also, do I understand it correctly that the first “base” template inherits from false_type, and the partial template specialization “adds” another inheritance level? (so does the partial specializations inheritance hierarchy look like this: false_type > true_type > IsIterable, where the definitions of false_type are hidden by true_type?)
No, they are completely unrelated types. A specialization of a class template does not inherit implicitly from the general case.
If you want to see this live, try adding a large non-static data member (eg int[100]) to the general case and then compare the sizeof the instantiated types. If the special case is smaller, it cannot possibly be derived from the general case.
Now onto my actual question. Why does the decltype expression have to evaluate to void?
It doesn't have to but in order to make this work, you'll also have to change the default for the base case from void to bool or whatever. Be aware, however, that an overloaded operator, could cause you strange surprises if you pick anything but void.
The technique was nicely explained in a talk Walter Brown gave at CppCon 2014. If you have two hours sparse, I strongly recommend you watch the recording of his talk:
Walter E. Brown, Modern Template Metaprogramming: A Compendium, Part I. CppCon 2014.
Walter E. Brown, Modern Template Metaprogramming: A Compendium, Part II. CppCon 2014
Syntax of Function Template
template <**class** T, ...>
returntype functionname(arguments)
{
.....
.....
}
I have two Questions?
Why the template parameter should be declared as a class type?(ie
with the use of class keyword)
When we declared it as a class type then what the thing the compiler
will do?
That's the usual confusion that arises from the usage of class in template arguments.
That class thing has nothing to do with classes; it merely says that the template accepts a type template argument (as opposed to integral1 template arguments), which can be any type, not only classes.
So, why did they choose class? Because they had to use a keyword that was surely not used in any C++ program and more or less "sounded good" - and class was ok, since it was already a reserved keyword in C++.
Notice that there's an alternative to class: the typename keyword. They are perfectly equivalent2, but typename in my opinion is much more clear, since the name just says "what follows is a type argument", without making you think that it must be a class.
Why both syntax are allowed? Because the typename keyword had do be introduced in the language later (when they noticed that it was necessary to add another keyword to disambiguate some declarations inside templates); then, it was "retrofitted" also for the template arguments declarations. This usage of the class keyword was kept for compatibility with programs/documentation written in the meantime.
here I say "integral" for simplicity, obviously I mean non-type template parameters in general (C++11, §14.1 ¶4).
There is no semantic difference between class and typename in a template-parameter.
(C++11, §14.1 ¶2)
Because that's the word that the language definiton tells you to use. 'class T' in this context means 'T is the name of some type', not 'T is the name of some class'.
I believe the rationale lies in a desire to not add yet another reserved word.
However, the language eventually added yet another reserved word: you can equivalently say 'typename T'.
According to the Standard, there are two keywords : class and typename. You can use any one of them in a template definition. Both has same meaning: when you write class (or typename) in a template definition, it means the user of the template has to pass a type as template argument to the template; it doesn't mean anything more than that. If it is a function template, then the template argument may be deduced (in some cases) from the argument to the function.
Note that you can pass standard types to template, not only classes:
template <class T, int N> class mysequence {..};
So, class keyword here tells the compiler to treat T as class. And N is treated as integer.
(1) Why the template parameter should be declared as a class type?
Not true entirely. You can use typename as well. :)
More importantly, you can declare const object of certain types also as a parameter. e.g.
template<int I> // <---- 'I' is not a class/typename
class A { ... };
...
A<3> obj;
(2) When we declared it as a class type then what the thing the compiler
will do?
Actually not much.
However, compiler checks the type is actually a type-name, when you invoke its objects. e.g.
template<class T>
class A { ... }; // ok ... not much to check
...
A<int> obj1; // compiler checks if 'int' is a type ---> yes
A<3> obj2; // compiler checks if '3' is a type ---> no