what template <class = typename T::type> means? Can you refer me to some blog, specification describing this?
The question originaly came from explanation of sfinae on cpp reference
template <typename A>
struct B { typedef typename A::type type; };
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename B<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo (int);
First, I'll explain typename T::type. This is simply the access of a member type. Here's an example of accessing a member type:
struct foo {
using bar = int;
};
int main() {
foo::bar var{};
// var has type int
}
So why the typename? It simply means we want to access a type. Since we are in a template and T is an unknown type, and foo::bar could also mean accessing a static variable. To disambiguate, we denote that we effectively want to access a type by explicitly typing typename.
Okay, now what does the class = means?
The class = means the same thing as typename =. When declaring template type parameters, we introduce then using class or typename:
template<typename A, typename B>
struct baz {};
But as any parameters in C++, the name is optional. I could have wrote this and the following is completely equivalent:
template<typename, typename>
struct baz {};
Also, you know in function parameters, we can specify default values? Like that:
void func(int a, int b = 42);
int main () {
func(10); // parameter b value is 42
// We are using default value
}
We can also omit parameter names:
void func(int, int = 42);
Just like function parameters, template parameters can have it's name omitted, and can have a default value.
template<typename, typename = float>
struct baz {};
baz<int> b; // second parameter is float
Putting it all together
Now we have this declaration:
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename B<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo (int);
Here we declare a function that takes an int as parameter, and has three template parameter.
The fist parameter is a simple named parameter. The name is T and it's a type template parameter. The second is also a type parameters, but it has no name. However, it has a default value of T::type, which is a member type of T. We are explicitly telling the compiler that T::type must be a member type of T by specifying typename. The third parameter is similar to the second.
This is where SFINAE kicks in: when a default parameter is used, but T::type as as a member type don't exist, how can you assign the second template parameter to that? We can't. If T::type don't exists, we cannot assign the second template parameter. But instead of making it an error, the compiler will simply try another function, because there is a chance another function would be callable.
This is quite similar to simple overloading. You have the f function. It takes a float parameter, an another overload that takes a std::string. Imagine you call f(9.4f). Does the compiler choke because a std::string is not constructible from a float? No! The compiler ain't stupid. It will try another overload, and will find the float version and call it. In SFINAE a similar analogy can be made. The compiler won't stop because there's some overload somewhere that needs an undefined type in a template parameter. It will try another overload.
Related
While answering one of SO questions I came across code like this:
template<class T, class... Ts> void foo();
template <class T, class T::value_type>
void foo() { }
It was presented as code specializing foo template function, which is not right, but that is not my issue here. I would like to know why does compiler allow construcion like this: class T::value_type in the template parameter. I mean, it is pretty clearly wrong, I cannot come up with any situation that scope operator may be part of the argument name (either template or function). And so I have two qestions:
Does standard allow it or is it an overlook in compilers?
If standard allows it, why is it so? Are there any use cases?
As mentioned in the comments, it's an elaborated type specifier. Best explained with an example:
int main() {
struct foo {}; // ok
int foo = 0; // ok
int test = foo; // ok, refers to variable 'foo'
foo a; // error, 'foo' refers to variable
struct foo b; // ok, 'struct' means that name lookup searches for classes only
}
In essence, you can think of them (struct/class, enum) as a more restricted typename, as they only allow classes or enums respectively. Also do note that typename is allowed in your original example!
template<class T, class... Ts> void foo();
template <class T, typename T::value_type> // Ok, value_type needs to be a type
// ^^^^^^^^^^^^^^^^^^^^^^^ it's a non-type template parameter
void foo() { }
It is needed when you have a type and a variable with the same name, or to specify what the thing is when you have a dependent name (i.e. in class T::value_type, value_type is a class, without the class before, it would have been a value. Normally, a typename is used.)
I cannot come up with any situation that scope operator may be part of the argument name
Your only thinking of type template parameters here; non-type template parameters can very well use a scope operator to name the type.
Consider classes with member variables, like these:
struct A {
int a;
char b;
};
struct B {
double c;
bool d;
};
Is it possible to declare a template class that accepts as its template
argument a pointer-to-member-object to any one of the members declared
in the above classes?
A class that accepts a generic pointer-to-member-object could be
declared and used as follows:
template<typename Class, typename Type, Type (Class::*Member)>
struct Magic
{ };
// Usage:
typedef Magic<A, int, &A::a> MagicWithA_a;
Unfortunately it is quite cumbersome having to pass in the Class and
Type template arguments every time to make the final pointer work.
Is there any way to have these arguments deduced by partial specialization,
for example? That is, how can the Magic class be declared to make the
below definitions work?
typedef Magic<&B::c> MagicWithB_c;
typedef Magic<&A::b> MagicWithA_b;
With C++17 you can use auto non-type template parameter:
template<auto p_member>
struct Magic
{ };
Before C++17 only the longer variant that you've implemented would work.
You can shorten it somewhat with specializations, yes. And if you don't mind resorting to macros you can have almost the syntax you want with c++11. First, a primary template specialization:
template<typename T, T pm>
struct MagicImpl; // Undefined for most types
Then a partial specialization for pointers to members, where we can freely add the parameters we wish to be deduced:
template<class Class, typename Type>
struct MagicImpl<Type Class::*, Type (Class::*Member)> {
};
And finally, we need to employ decltype to get the pointer to member type out of the pointer-to-member expression, which we hide behind the aforementioned macro:
#define MAGIC(...) MagicImpl<decltype(__VA_ARGS__), __VA_ARGS__>
And you can use it as follows:
typedef MAGIC(&B::c) MagicWithB_c;
typedef MAGIC(&A::b) MagicWithA_b;
I have come across class = std::enbale_if<condition, type>::type couple times in template parameter list, I know what std::enable_if<B,T>::type does but not sure what class = does ? I mean I know class type_name = type but why there is no type name in class = ? when would I use it ?
Edit: This example from here
template <class T, class U, class = typename enable_if
<is_lvalue_reference<T>::value ? is_lvalue_reference<U>::value : true>::type>
inline T&& forward(U&& u)
{
return static_cast<T&&>(u);
}
This is SFINAE (substitution failure is not an error). Below are two examples, where constraints are put on struct and class to disallow instantiations with types that would not be correct.
but not sure what class = does ? I mean I know class type_name = type but why there is no type name in class = ? when would I use it ?
This is an unnamed template parameter type, you could give it a name but its not needed as this parameter is only used during overload resoultion (to remove non valid candidates). Actually giving it a name might show warnings.
http://coliru.stacked-crooked.com/a/25162a3eb107943f
// Allow only T to be floating type (!! actually static_assert would be preferred here)
template<class T,
class = typename std::enable_if<std::is_floating_point<T>::value>::type>
struct foo {
};
template <class T,
class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even(T i) {return !bool(i%2);}
int main()
{
// foo<int> f; //error
foo<float> f; //ok
//is_even(1.1); //error
is_even(1); //ok
return 0;
}
[edit]
I must say above examples are not the best, and using static_assert would be recomended as it allows to generate informative error messages. SFINAE should not be used for parameter validation but rather for selecting another implementation depending on provided types.
It's an unnamed template type parameter. Just like you can leave out the variable name in unused function parameters, you can leave out the parameter name in unreferenced template parameters.
In the specific case of std::enable_if<...>::type, this is used solely for SFINAE purposes. Specifically, if the boolean in the enable_if is false, template parameter deduction will fail.
I was looking through the documentation of SFINAE and there was this template declaration:
template<typename SomeType>
struct inner_type { typedef typename SomeType::type type; };
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename inner_type<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo(int) {}
Specifically, I'm asking about class = typename T::type. What's the point of declaring an unnamed class?
Because of the comment I thought that this will result in a compiler error when T doesn't have a member type, but that isn't the case, as foo<int, int, int>(0); compiles fine.
On the other hand
template<class T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
void foo(T t) {}
doesn't compile if T is signed, and compiles if T is unsigned.
What am I missing here?
foo<int, int, int>(0); compiles fine.
Because you specify the 2nd template argument, then the default template argument (i.e. typename T::type) won't be used, then won't trigger compile error.
If you just write foo<int>(0); to make the default template argument to be used, compile will fail.
LIVE
And it's same for your 2nd sample too.
What's the point of declaring an unnamed class?
Because the template parameter won't be used for the template implementation.
Is it possible to define a template which takes a single pointer parameter and extracts the type pointed to?
extern int three = 3;
typename examine<&three>::pointed_type // int
There's already std::remove_pointer<T>::type in <type_traits> (C++11) that you can use.
Yes, you would use partial specialization to achieve this, similar to the following:
template<typename T>
struct examine {
typedef T pointed_type;
};
template<typename T>
struct examine<T*> {
typedef T pointed_type;
};
To refer to your comment about usage of non-type template parameters, consider the following:
template<typename T, T* p>
struct foo { };
extern int three = 3;
foo<decltype(three), &three> bar;
As you can see, foo can indeed take a template parameter of &x, but to do so we first need to give it a type parameter (or restrict it to int*, which does not help at all). If this is not done, the name T is not that of any type when the template parameter p is defined.
There is no way at all to perform any kind of automatic deduction of T, since that would require the template arguments to be defined in the other order.
The closest you can get without using decltype is by defining a function template that gets a non-template argument that will cause the template type parameter to be deduced like so
template<typename T>
void deduce_argument_type(T const&)
{
// here, the type T is (close to) that of your argument
}
deduce_argument_type(&three); // uses deduce_argument_type<int*>
Again, you cannot use this to get around the restriction on defining T before passing &three, since template parameter deduction only deduces any types to the right of those template parameters you passed.