"Name The Template Parameter" Odd Definition - c++

template <bool, class t, class u>// why is bool here,class booltype=bool. Are they equivalent?
struct if_
{
typedef typename t type;
};
template<class t, class u>
struct if_<false, t, u> // what does the <false,t,u> mean?
{
typedef typename u type;
};
The code if from an article named "name the template parameter ". I can't understand the definition of the both struct.

EDIT: Moved the most important part first:
if_<true, int, double>::type; // this is int
if_<false, int, double>::type; // this is double
This conveniently defines a type that can conditionally be defined at compile-time.
Old Answer:
template <bool, class t, class u>// why is bool here,class booltype=bool. Are they equivalent?
struct if_
{
typedef typename t type;
};
This means the first parameter passed to the template is a bool.
It's not equivalent to class booltype=bool. That would be a default typename for the template.
template<class t, class u>
struct if_<false, t, u> // what does the <false,t,u> mean?
{
typedef typename u type;
};
This is a specialization of your struct. If the first parameter passed to the template is false, this it what the struct is defined as.
Basically, assume:
if_<true, int, double> x;
//is defined as
//struct if_
//{
// typedef int type;
//};
and
if_<false, int, double> x;
//is defined as
//struct if_
//{
// typedef double type;
//};
The notation basically tells what to typedef - if the first parameter is true, typedef the second parameter, otherwise the third.

The first is a general template definition of type if_. The bool is a template parameter (template parameters can be types or integral values)
The second is a partial specialization of the same template. PArtial specialization means that you set some of the template parameters, but not all.
Which one gets used is decided like this: if there is an excplicit specialization for the actual template parameters, that specialization is used, otherwise the non-specialized (the forst definition in this case). The non-specialized version acts a "default" selection

struct if_<false, t, u> is a partial sspecialization of the primary template if_ which is instantiated by the compiler whenever the first parameter of the template has the value false, regardless of the other two "type-parameters". You're dealing with "non-type" template parameters here. The primary purpose of this construction is to select either the type t or type u, depending on the value of the first parameter, so that
if_<true, int, double>::type
will be of type int
and
if_<false, int, double>::type
will be of type double. Admittedly quite difficult to grasp if it's your "first contact" with c++ meta-programming, but basically you get an equivalent of the traditional if statement evaluated by the compiler at compile-time.

Related

How can I get the integral type of a template argument regardless of whether it's an enum or not

I'm getting the error:
Error C2154 '_Ty': only enumeration type is allowed as an argument to compiler intrinsic type trait '__underlying_type'
I thought it shouldn't be resolving underlying_type to underlying_type, because I first check whether T is an enum. Here is the code:
template <typename T>
struct Foo
{
static inline constexpr bool isArgIntegral = std::is_integral<T>::value;
static inline constexpr bool isArgEnum = std::is_enum_v<T>;
using integral_underlying_type = std::conditional<isArgEnum, std::underlying_type_t<T>, T>;
};
int main()
Foo<int> g; // only enumeration type is allowed as an argument to compiler intrinsic type trait '__underlying_type'
}
So is it the case that in a call to std::conditional, instead of first checking the condition (1st argument), it creates the classes of the 2nd and 3rd arguments regardless of the condition, and hence why I'm getting the error that I can't call underlying_type with an 'int'?
How do I go about getting the integral type of the T template argument, whether it's an integral or an enum?
Edit: My next attempt is to place the typedef in an if constexpr:
if constexpr (std::is_enum_v<T>)
{
using integral_underlying_type = std::underlying_type_t<T>;
// Now std::underlying_type_t won't be called at all unless T is enum, right?
}
Unfortunately, std::integral_underlying_type is not SFINAE friendly until C++20.
You can then delay instantiation of std::underlying_type<T>::type:
template <typename T>
struct Foo
{
using integral_underlying_type =
typename std::conditional_t<std::is_enum_v<T>,
std::underlying_type<T>,
type_identity<T>>::type;
};
Notice the double ::type in std::conditional<cond, T1, T2>::type::type (hidden with _t). The extra ::type is done outside the conditional instead of inside (and so the need of type_identity).
Yes. The problem is that whether the condition isArgEnum is true or false (i.e. whether T is enum or not), std::underlying_type_t<T> has to be specified as the template argument for std::conditional.
You can apply partial specialization like
template <typename T, typename = void>
struct Foo
{};
template <typename T>
struct Foo<T, std::enable_if_t<std::is_enum_v<T>>>
{
using integral_underlying_type = std::underlying_type_t<T>;
};
template <typename T>
struct Foo<T, std::enable_if_t<std::is_integral_v<T>>>
{
using integral_underlying_type = T;
};

What this template syntax "typename = T" mean?

Sometimes I see syntax like this.
template<typename T,typename = int>
int foo(){
//...
}
what part typename = int mean?
Where it can be used?
foo has two template arguments. The first is called T and the second is unnamed and defaults to int.
In your piece of code alone there is no reason to use the second argument. Unnamed template arguments often come up with SFINAE. An example from cppreference:
// primary template handles non-referenceable types:
template<class T, class = void>
struct reference_traits {
using add_lref = T;
using add_rref = T;
};
// specialization recognizes referenceable types:
template<class T>
struct reference_traits<T, std::void_t<T&>> {
using add_lref = T&;
using add_rref = T&&;
};
template<class T>
using add_lvalue_reference_t = typename reference_traits<T>::add_lref;
template<class T>
using add_rvalue_reference_t = typename reference_traits<T>::add_rref;
The only reason for the primary template to have a second argument is that it can be specialized. When possible the more specialized specialization is instantiatied. If this fails (because T& is not valid) then "substitution failure is not an error" (SFINAE) kicks in and the primary template is instantiated instead.
A simpler example of unnamed argument is when you want a template argument merely as a tag to distinguish different instantiations:
template<typename = int>
struct bar {
// ...
};
Even if the implementation of bar does not depend on the template argument you might want to have bar<double> and bar<std::string> be two distinct types.
this is rarely used ...
but this is the default value for the typename but you don't need it here because the compiler itself can overload the function automatically and get the right type for the right arguments you passed !
also it type for what typename ? it's not makes sense here !
it used when you are using nested template ...
I found out in the original reference for C++ :
The template parameter lists of template template parameters can have
their own default arguments, which are only in effect where the
template template parameter itself is in scope:
// class template, with a type template parameter with a default
template<typename T = float> struct B {};
// template template parameter T has a parameter list, which
// consists of one type template parameter with a default
template<template<typename = float> typename T> struct A
{
void f();
void g();
};
// out-of-body member function template definitions
template<template<typename TT> class T>
void A<T>::f()
{
T<> t; // error: TT has no default in scope
}
template<template<typename TT = char> class T>
void A<T>::g()
{
T<> t; // ok: t is T<char>
}
this is the link

Why do we need a template specialization for a template-template parameter

Take as an example this code to determine the length of a type list:
template <class... Types>
class type_list {};
template <class TypeList>
struct type_list_length; // <---
template <template <class...> class type_list, class... Types>
struct type_list_length<TypeList<Types...>>
{
static constexpr std::size_t value = sizeof...(Types);
};
Godbolt
Why do we need the marked declaration? I tried to compile the code without it in several compilers but are always getting errors.
But why do we need the specialization in the first place?
Because you use the class as follows
std::cout << type_list_length<type_list<int, long, long long>>::value;
// ...........................^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- template argument
or also
std::cout << type_list_length<std::tuple<int, long, long long>>::value;
// ...........................^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- template argumen
or in a similar way.
Observe the template argument: in both cases is a type; type_list<int, long, long long> in first case, std::tuple<int, long, long long>.
So you can't declare type_list_length as receiving a template-template type
template <template <class...> class type_list, class... Types>
struct type_list_length // <--- doesn't work
{
static constexpr std::size_t value = sizeof...(Types);
};
because you should call it passing a template-template parameter followed by a variadic list of templates; I mean... you should use it as follows
std::cout << type_list_length<type_list, int, long, long long>::value;
std::cout << type_list_length<std::tuple, int, long, long long>::value;
but, this way, you loose the power of the class: extract and count the template parameter of the type parameter.
So you need first declare type_list_length as receiving a type
template <typename> // no template parameter name needed here (not used)
struct type_list_length;
and then declare and define a specialization in case the received type is a template-template with arguments
template <template <typename...> class type_list, typename... Types>
struct type_list_length<TypeList<Types...>>
{ // ...................^^^^^^^^^^^^^^^^^^ the parameter is a type
static constexpr std::size_t value = sizeof...(Types);
};
The short answer is that without the primary template we cannot write a specialization.
The longer answer is: how would you extract Types... from the instantiation of a templated type without a specialization? You cannot.
Here's an attempt:
template <template <class...> class type_list, class... Types>
struct type_list_length
{
static constexpr std::size_t value = sizeof...(Types);
};
We can do this:
type_list_length<type_list, int, double, float>::value
But not this:
using MyListType = type_list<int, double, float>;
type_list_length<MyListType>::value;
Because our template expects a template-template parameter and some types, so we're forced to accept just a single type to match MyListType:
template <class T>
struct type_list_length
{
static constexpr std::size_t value = // ????;
};
But now we're faced with another issue. How do we assign value? We need some way to extract the template arguments for MyListType, or at least the count.
We need a way to match a single type and the arguments it is templated on. Hence, we need match just a single type AND its template parameters.
template <class TypeList>
struct type_list_length;
template <template <class...> class type_list, class... Types>
struct type_list_length<TypeList<Types...>>
{
static constexpr std::size_t value = sizeof...(Types);
};
The first (incomplete) type is our primary template. It allows us to start matching a single type, like MyListType.
The second (complete) type is our specialization. it allows us to match a single type AND if it's a templated type, match the types used as template parameters for it.
By leaving the first type incomplete, we demonstrate our intention to ONLY allow the specialization to be valid.

Variadic template partial specialisation "not more specialised" in g++ 7.1.1

I have an equivalent to std::integer_sequence (we're not using C++14 yet). I also have two helper classes that remove or add a leading number.
// Sequence type
template <typename Type, Type ... Values>
struct Sequence
{
using value_type = Type;
};
// Pop a value off of the start of a sequence
template <class Class>
struct SequencePop;
template <typename Type, Type Value, Type ... Values>
struct SequencePop<Sequence<Type, Value, Values ...>>
{
using type = Sequence<Type, Values ...>;
};
// Push a value on to the start of a sequence
template <class Class, typename Class::value_type Value>
struct SequencePush;
template <typename Type, Type Value, Type ... Values>
struct SequencePush<Sequence<Type, Values ...>, Value>
{
using type = Sequence<Type, Value, Values ...>;
};
SequencePop is considered valid in all compilers I have tried (g++ 6.4.1, g++ 7.1.1, clang++ 4.0.1). SequencePush doesn't compile with g++ 7.1.1. The error message is as follows.
test.cpp:24:8: error: partial specialization ‘struct SequencePush<Sequence<Type, Values ...>, Value>’ is not more specialized than [-fpermissive]
struct SequencePush<Sequence<Type, Values ...>, Value>
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:21:8: note: primary template ‘template<class Class, typename Class::value_type Value> struct SequencePush’
struct SequencePush;
Is g++ 7.1.1 correct to reject this code, and if so, how does one tell that SequencePush is not "more specialised" than the primary template?
It should be
template <typename Type,
Type ... Values,
typename Sequence<Type, Values ...>::value_type Value>
struct SequencePush<Sequence<Type, Values ...>, Value>
{
using type = Sequence<Type, Value, Values ...>;
};
Demo
as Class::value_type and Type from Sequence<Type, Values ...> are not "related".
Indeed, the general template SequencePush is parameterized by one template parameter (Class) but your specialization is parameterized by two (Type and Value). In the general template, Type is not a parameter as it is deduced from Class.
A working but not satisfactory solution would be:
template <class Class, typename Type, Type Value>
struct SequencePush;
template <typename Type, Type Value, Type ... Values>
struct SequencePush<Sequence<Type, Values ...>, Type, Value>
{
using type = Sequence<Type, Value, Values ...>;
};
demo

Unnamed class/typename in template arguments

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.