I'm not sure if this is possible, but I would like to count the number of template arguments of any class like:
template <typename T>
class MyTemplateClass { ... };
template <typename T, typename U>
class MyTemplateClass2 { ... };
such that template_size<MyTemplateClass>() == 1 and template_size<MyTemplateClass2>() == 2. I'm a beginner to template templates, so I came up with this function which of course does not work:
template <template <typename... Ts> class T>
constexpr size_t template_size() {
return sizeof...(Ts);
}
because Ts can not be referenced. I also know that it might come to problems when handling variantic templates, but that is not the case, at least for my application.
Thx in advance
There is one...
° Introduction
Like #Yakk pointed out in his comment to my other answer (without saying it explicitly), it is not possible to 'count' the number of parameters declared by a template. It is, on the other hand, possible to 'count' the number of arguments passed to an instantiated template.
Like my other answer shows it, it is rather easy to count these arguments.
So...
If one cannot count parameters...
How would it be possible to instantiate a template without knowing the number of arguments this template is suppose to receive ???
Note
If you wonder why the word instantiate(d) has been stricken throughout this post,
you'll find its explanation in the footnote. So keep reading... ;)
° Searching Process
If one can manage somehow to try to instantiate a template with an increasing number of arguments, and then, detect when it fails using SFINAE (Substitution Failure Is Not An Error), it should be possible to find a generic solution to this problem... Don't you think ?
Obviously, if one wants to be able to also manage non-type parameters, it's dead.
But for templates having only typename parameters...
There is one...
Here are the elements with which one should be able to make it possible:
A template class declared with only typename parameters can receive any type as argument. Indeed, although there can have specializations defined for specific types,
a primary template cannot enforce the type of its arguments.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The above statement might no longer be true from C++20 concepts.
I cannot try ATM, but #Yakk seems rather confident on the subject. After his comment:
I think concepts breaks this. "a primary template cannot enforce the type of its arguments." is false.
He might be right if constraints are apply before the template instantiation. But...
By doing a quick jump to the introduction to Constraints and concepts, one can read, after the first code example:
"Violations of constraints are detected at compile time, early in the template instantiation process, which leads to easy to follow error messages."
To be confirmed...
It is perfectly possible to create a template having for sole purpose to
be instantiated with any number of arguments. For our use case here, it might contain only ints... (let's call it IPack).
It is possible to define a member template of IPack to define the Next IPack by adding an int to the arguments of the current IPack. So that one can progressively increase its number of arguments...
Here is maybe the missing piece. It is maybe something that most people don't realize.
(I mean, most of us uses it a lot with templates when, for example, the template accesses a member that one of its arguments must have, or when a trait tests for the existence of a specific overload, etc...)
But I think it might help in finding solutions sometimes to view it differently and say:
It is possible to declare an arbitrary type, built by assembling other types, for which the evaluation by the compiler can be delayed until it is effectively used.
Thus, it will be possible to inject the arguments of an IPack into another template...
Lastly, one should be able to detect if the operation succeeded with a testing trait making use of decltype and std::declval. (note: In the end, none of both have been used)
° Building Blocks
Step 1: IPack
template<typename...Ts>
struct IPack {
private:
template<typename U> struct Add1 {};
template<typename...Us> struct Add1<IPack<Us...>> { using Type = IPack<Us..., int>; };
public:
using Next = typename Add1<IPack<Ts...>>::Type;
static constexpr std::size_t Size = sizeof...(Ts);
};
using IPack0 = IPack<>;
using IPack1 = typename IPack0::Next;
using IPack2 = typename IPack1::Next;
using IPack3 = typename IPack2::Next;
constexpr std::size_t tp3Size = IPack3::Size; // 3
Now, one has a means to increase the number of arguments,
with a convenient way to retrieve the size of the IPack.
Next, one needs something to build an arbitrary type
by injecting the arguments of the IPack into another template.
Step 2: IPackInjector
An example on how the arguments of a template can be injected into another template.
It uses a template specialization to extract the arguments of an IPack,
and then, inject them into the Target.
template<typename P, template <typename...> class Target>
struct IPackInjector { using Type = void; };
template<typename...Ts, template <typename...> class Target>
struct IPackInjector<IPack<Ts...>, Target> { using Type = Target<Ts...>; };
template<typename T, typename U>
struct Victim;
template<typename P, template <typename...> class Target>
using IPInj = IPackInjector<P, Target>;
//using V1 = typename IPInj<IPack1, Victim>::Type; // error: "Too few arguments"
using V2 = typename IPInj<IPack2, Victim>::Type; // Victim<int, int>
//using V3 = typename IPInj<IPack3, Victim>::Type; // error: "Too many arguments"
Now, one has a means to inject the arguments of an IPack
into a Victim template, but, as one can see, evaluating Type
directly generates an error if the number of arguments does not
match the declaration of the Victim template...
Note
Have you noticed that the Victim template is not fully defined ?
It is not a complete type. It's only a forward declaration of a template.
The template to be tested will not need to be a complete type
for this solution to work as expected... ;)
If one wants to be able to pass this arbitrary built type to some detection trait one will have to find a way to delay its evaluation.
It turns out that the 'trick' (if one could say) is rather simple.
It is related to dependant names. You know this annoying rule
that enforces you to add ::template everytime you access a member template
of a template... In fact, this rule also enforces the compiler not to
evaluate an expression containing dependant names until it is
effectively used...
Oh I see ! ...
So, one only needs to prepare the IPackInjectors without
accessing its Type member, and then, pass it to our test trait, right ?
It could be done using something like that:
using TPI1 = IPackInjector<IPack1, Victim>; // No error
using TPI2 = IPackInjector<IPack2, Victim>; // No error
using TPI3 = IPackInjector<IPack3, Victim>; // No error
Indeed, the above example does not generate errors, and it confirms
that there is a means to prepare the types to be built and evaluate
them at later time.
Unfortunately, it won't be possible to pass these pre-configured
type builders to our test trait because one wants to use SFINAE
to detect if the arbitrary type can be instantiated or not.
And this is, once again, related to dependent name...
The SFINAE rule can be exploited to make the compiler silently
select another template (or overload) only if the substitution
of a parameter in a template is a dependant name.
In clear: Only for a parameter of the current template instantiation.
Hence, for the detection to work properly without generating
errors, the arbitrary type used for the test will have to be
built within the test trait with, at least, one of its parameters.
The result of the test will be assigned to the Success member...
Step 3: TypeTestor
template<typename T, template <typename...> class C>
struct TypeTestor {};
template<typename...Ts, template <typename...> class C>
struct TypeTestor<IPack<Ts...>, C>
{
private:
template<template <typename...> class D, typename V = D<Ts...>>
static constexpr bool Test(int) { return true; }
template<template <typename...> class D>
static constexpr bool Test(...) { return false; }
public:
static constexpr bool Success = Test<C>(42);
};
Now, and finally, one needs a machinery that will successively try
to instantiate our Victim template with an increasing number of arguments. There are a few things to pay attention to:
A template cannot be declared with no parameters, but it can:
Have only a parameter pack, or,
Have all its parameters defaulted.
If the test procedure begins by a failure, it means that the template must take more arguments. So, the testing must continue until a success, and then, continue until the first failure.
I first thought that it might make the iteration algorithm using template specializations a bit complicated... But after having thought a little about it, it turns out that the start conditions are not relevant.
One only needs to detect when the last test was true and next test will be false.
There must have a limit to the number of tests.
Indeed, a template can have a parameter pack, and such a template can receive an undetermined number of arguments...
Step 4: TemplateArity
template<template <typename...> class C, std::size_t Limit = 32>
struct TemplateArity
{
private:
template<typename P> using TST = TypeTestor<P, C>;
template<std::size_t I, typename P, bool Last, bool Next>
struct CheckNext {
using PN = typename P::Next;
static constexpr std::size_t Count = CheckNext<I - 1, PN, TST<P>::Success, TST<PN>::Success>::Count;
};
template<typename P, bool Last, bool Next>
struct CheckNext<0, P, Last, Next> { static constexpr std::size_t Count = Limit; };
template<std::size_t I, typename P>
struct CheckNext<I, P, true, false> { static constexpr std::size_t Count = (P::Size - 1); };
public:
static constexpr std::size_t Max = Limit;
static constexpr std::size_t Value = CheckNext<Max, IPack<>, false, false>::Count;
};
template<typename T = int, typename U = short, typename V = long>
struct Defaulted;
template<typename T, typename...Ts>
struct ParamPack;
constexpr std::size_t size1 = TemplateArity<Victim>::Value; // 2
constexpr std::size_t size2 = TemplateArity<Defaulted>::Value; // 3
constexpr std::size_t size3 = TemplateArity<ParamPack>::Value; // 32 -> TemplateArity<ParamPack>::Max;
° Conclusion
In the end, the algorithm to solve the problem is not that much complicated...
After having found the 'tools' with which it would be possible to do it, it only was a matter, as very often, of putting the right pieces at the right places... :P
Enjoy !
° Important Footnote
Here is the reason why the word intantiate(d) has been stricken at the places where it was used in relation to the Victim template.
The word instantiate(d) is simply not the right word...
It would have been better to use try to declare, or to alias the type of a future instantiation of the Victim template.
(which would have been extremely boring) :P
Indeed, none of the Victim templates gets ever instantiated within the code of this solution...
As a proof, it should be enough to see that all tests, made in the code above, are made only on forward declarations of templates.
And if you're still in doubt...
using A = Victim<int>; // error: "Too few arguments"
using B = Victim<int, int>; // No errors
template struct Victim<int, int>;
// ^^^^^^^^^^^^^^^^
// Warning: "Explicit instantiation has no definition"
In the end, there's a full sentence of the introduction which might be stricken, because this solution seems to demonstrate that:
It is possible to 'count' the number of parameters declared by a template...
Without instantiation of this template.
#include <utility>
#include <iostream>
template<template<class...>class>
struct ztag_t {};
template <template<class>class T>
constexpr std::size_t template_size_helper(ztag_t<T>) {
return 1;
}
template <template<class, class>class T>
constexpr std::size_t template_size_helper(ztag_t<T>) {
return 2;
}
template <typename T>
class MyTemplateClass { };
template <typename T, typename U>
class MyTemplateClass2 { };
template<template<class...>class T>
struct template_size:
std::integral_constant<
std::size_t,
template_size_helper(ztag_t<T>{})
>
{};
int main() {
std::cout << template_size<MyTemplateClass>::value << "\n";
std::cout << template_size<MyTemplateClass2>::value << "\n";
}
I know of no way without writing out the N overloads to support up to N arguments.
Live example.
Reflection will, of course, make this trivial.
° Before Reading This Post
This post does not answer to "How to get the number of parameters",
it answers to "how to get the number of arguments"...
It is let here for two reasons:
It might help someone who would have mixed up (like I did)
the meaning of parameters and arguments.
The techniques used in this post are closely related to the ones used
to produce the correct answer I've posted as a separate answer.
See my other answer for finding "the number of parameters" of a template.
The answer of Elliott looks more like what one usually does (though the primary template should be fully defined and "do something" IMHO). It uses a template specialization for when a template is passed as argument to the primary template.
Meanwhile, the answer of Elliott vanished...
So I've posted something similar to what he showed below.
(see "Generic Solution")
But, just to show you that you weren't that far from a working solution, and, because I noticed that you have used a function for your try, and, you declared this function constexpr, you could have written it like that:
Note
It is a 'fancy solution', but it works...
template <typename T> class MyTemplateClass {};
template <typename T, typename U> class MyTemplateClass2 {};
template <template <typename...> class T, typename...Ts>
constexpr const size_t template_size(T<Ts...> && v)
{
return sizeof...(Ts);
}
// If the target templates are complete and default constructible.
constexpr size_t size1 = template_size(MyTemplateClass<int>{});
constexpr size_t size2 = template_size(MyTemplateClass2<int, short>{});
// If the target templates are complete but NOT default constructible.
constexpr size_t size3 = template_size(decltype(std::declval<MyTemplateClass<int>>()){});
constexpr size_t size4 = template_size(decltype(std::declval<MyTemplateClass2<int, short>>()){});
Explanation
You said "because Ts can not be referenced", which is true and false, because of the way you made the declaration of template_size.
That is, a template template parameter cannot declare parameters itself (where you placed Ts in the declaration of the function template). It is allowed to do so to give a clue of what the template argument is expected to receive as argument, but it is not a declaration of a parameter name for the current template declaration...
(I hope it's clear enough) ;)
Obviously, it might be a little bit over complicated, but it worth knowing I think that such a construct is possible also... ;)
Generic Solution
template <typename T> class MyTemplateClass {};
template <typename T, typename U> class MyTemplateClass2 {};
template<typename T>
struct NbParams { static constexpr std::size_t Value = 0; };
template<template <typename...> class C, typename...Ts>
struct NbParams<C<Ts...>> { static constexpr std::size_t Value = sizeof...(Ts); };
constexpr size_t size1 = NbParams<MyTemplateClass<int>>::Value;
constexpr size_t size2 = NbParams<MyTemplateClass2<int, short>>::Value;
That is the regular way one does this kind of things... ;)
Related
template<typename T, size_t M, size_t K, size_t N, typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0>
void fastor2d(){//...}
I copied this line of code from cpp-reference(only the std::enable_if part, i do need T and all three of the size_t's), because i would like to use this function only when floating_types are used on it ... it does not compile.
Could somebody explain to me, why, and what it even does? While i am at it, how do you call this function afterwards?
Every tutorial or question here on SO gets bombed with answers, and that is great, but to someone who does not understand jacks*** of what is happening, even those are not really helpful.(sry, if possibly slightly agitated or aggressive)
EDIT: i greatly appreciate all answers as of now, i realize that my wording might have been a bit off ... i understand what a template parameter is, and know the difference between runtime and compiletime etc, but i just cant get a good grasp of the syntax behind std::enable_if
EDIT2:
template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_integral<T>::value>>
void fastor2d(){
Fastor::Tensor<T,M,K> A; A.randInt();
}
This is literally the only thing i need changed. Notice the random() part
template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void fastor2d(){
Fastor::Tensor<T,M,K> A; A.random();
}
I'll try to explain this as simple as possible not to go into the language details too much since you asked for it.
Template arguments are compile time arguments (they do not change during the run-time of your application). Function arguments are run-time and have a memory address.
Calling this function would look something like this:
fastor2d<Object, 1, 2, 3>();
In the <> brackets you see the compile-time arguments or more accurately the template parameters, and the function in this case takes 0 runtime arguments in the () brackets. The last compile time argument has a default argument which is used to check whether the function should compile at all (enable_if type). If you want to know more clearly what enable if does you should search for the term SFINAE, which is a template metaprogramming technique used to determine whether a function or class should exist or not.
Here is a short SFINAE example:
template<typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void function(T arg)
{
}
function(0.3f); //OK
function(0.0); //OK double results in std::is_floating_point<double>::value == true
function("Hello"); //Does not exist (T is not floating point)
The reason the third function call fails, is because the function does not exist. This is because the enable if caused the function not to exist when the compile-time bool that is passed in as its' template argument is false.
std::is_floating_point<std::string>::value == false
Do note that a lot of people agree that the SFINAE syntax is horrible and that a lot of SFINAE code will not be necessary anymore with the introduction of concepts and constraints in C++ 20.
Rather than a top-down approach starting with you code snippet, I'll take a bottom-up approach to explain some important details about templates and what tools and techniques are involved.
At heart, templates are a tool that let you write C++ code that applies to a range of possible types, not strictly for a fixed type. In a statically-typed language, this is firstly a great tool for reusing code without sacrificing type safety, but in C++ in particular, templates are very powerful because they can be specialized.
Every template declaration begins with the keyword template, and a list of type or non-type (i.e value) parameters. Type parameters use the special keyword typename or class, and are used to let your code work over a range of types. Non-type parameters simply use the name of an existing type, and these let you apply your code to a range of values that are known at compile-time.
A very basic templated function might look like the following:
template<typename T> // declare a template accepting a single type T
void print(T t){ // print accepts a T and returns void
std::cout << t; // we can't know what this means until the point where T is known
}
This lets us reuse code safely for a range of possible types, and we can use it as follows:
int i = 3;
double d = 3.14159;
std::string s = "Hello, world!";
print<int>(i);
print<double>(d);
print<std::string>(s);
The compiler is even smart enough to deduce the template parameter T for each of these, so you can safely get away with the following, functionally identical code:
print(i);
print(d);
print(s);
But suppose you want print to behave differently for one type. Suppose, for example, you have a custom Point2D class that needs special handling. You can do this with a template specialization:
template<> // this begins a (full) template specialization
void print<Point2D>(Point2D p){ // we are specializing the existing template print with T=Point2D
std::cout << '(' << p.x << ',' << p.y << ')';
}
Now, anytime we use print with T=Point2D, the specialization is chosen. This is really useful, for example, if the generic template just doesn't make sense for one specific type.
std::string s = "hello";
Point2D p {0.5, 2.7};
print(s); // > hello
print(p); // > (0.5,2.7)
But what if we want to specialize a template for many types at once, based on a simple condition? This is where things become a little meta. First, let's try to express a condition in a way that lets them be used inside templates. This can be a little tricky because we need compile-time answers.
The condition here will be that T is a floating point number, which is true if T=float or T=double and false otherwise. This is actually fairly simple to achieve with template specialization alone.
// the default implementation of is_floating_point<T> has a static member that is always false
template<typename T>
struct is_floating_point {
static constexpr bool value = false;
};
// the specialization is_floating_point<float> has a static member that is always true
template<>
struct is_floating_point<float> {
static constexpr bool value = true;
};
// the specialization is_floating_point<double> has a static member that is always true
template<>
struct is_floating_point<double> {
static constexpr bool value = true;
}
Now, we can query any type to see if it's a floating point number:
is_floating_point<std::string>::value == false;
is_floating_point<int>::value == false;
is_floating_point<float>::value == true;
is_floating_point<double>::value == true;
But how can we use this compile-time condition inside another template? How can we tell the compiler which template to choose when there are many possible template specializations to choose from?
This is achieved by taking advantage of a C++ rule called SFINAE, which in basic English, says, "when there are many possible template specializations to choose from, and the current one doesn't make sense*, just skip it and try the next one."
There's a list of errors, when attempting to substitute template arguments into templated code, that cause the template to be ignored without an immediate compiler error. The list is a bit long and complex.
One possible way that a template doesn't make sense is if it tries to use a type that doesn't exist.
template<typename T>
void foo(typename T::nested_type x); // SFINAE error if T does not contain nested_type
This is the exact same trick that std::enable_if uses under the hood. enable_if is a template class accepting a type T and a bool condition, and it contains a nested type type equal to T only when the condition is true. This is also pretty easy to achieve:
template<bool condition, typename T>
struct enable_if {
// no nested type!
};
template<typename T> // partial specialization for condition=true but any T
struct enable_if<true, T> {
typedef T type; // only exists when condition=true
};
Now we have a helper that we can use in place of any type. If the condition we pass is true, then we can safely use the nested type. If the condition we pass is false, then the template is no longer considered.
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type // This is the return type!
numberFunction(T t){
std::cout << "T is a floating point";
}
template<typename T>
typename std::enable_if<!std::is_floating_point<T>::value, void>::type
numberFunction(T t){
std::cout << "T is not a floating point";
}
I completely agree that std::enable_if<std::is_floating_point<T>::value, void>::type is a messy way to spell out a type. You can read it as "void if T is floating point, and otherwise stop and try the next overload"
Finally, to take apart your example:
// we are declaring a template
template<
typename T, // that accepts some type T,
size_t M, // a size_t M,
size_t K, // a size_t K,
size_t N, // a size_t N,
// and an unnamed non-type that only makes sense when T is a floating point
typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0
>
void fastor2d(){//...}
Note the = 0 at the end. That's simply a default value for the final template parameter, and it lets you get away with specifying T, M, K, and N but not the fifth parameter. The enable_if used here means that you can provide other templates called fastor2d, with their own sets of conditions.
First of all, I'll rewrite your function in a working form
template <typename T, size_t M, size_t K, size_t N,
std::enable_if_t<std::is_floating_point<T>::value, int> = 0>
void fastor2d() // ..........................................^^^ int, not T
{ }
The point is that I've changed the second template argument of std::enable_if_t form T to int.
I've also removed the typename before std::enable_if_t but isn't important: the typename is implicit in the _t at the end of std::enable_if_t, introduced from C++14. In C++11 the correct form is
// C++11 version
typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0
// ^^^^^^^^ no _t ^^^^^^
But why it works?
Start from the name: SFINAE.
Is a short form for "Substitution Failure Is Not An Error".
It's a C++ rule so that when you write some thing as
template <int I, std::enable_if_t< I == 3, int> = 0>
void foo ()
{ }
and I is 3, the condition of std::enable_if_t is true so std::enable_if_t< I == 3, int> is substituted with int so foo() is enabled but when I isn't 3, the condition of std::enable_if_t if false so std::enable_if_t< I == 3, int> is not substituted so foo() isn't enabled but this ins't an error (if, through overloading, there is another foo() function, enabled, that matches the call, obviously).
So where is the problem in your code?
The problem is that std::enable_if_t is substituted, when the first template parameter is true, with the second parameter.
So if you write
std::enable_if_t<std::is_floating_point<T>::value, T> = 0
and you call
fastor2d<float, 0u, 1u, 2u>();
the std::is_floating_point<float>::value (but you can also use the shorter form std::is_floating_point_v<T> (_v and not ::value)) so the substitution take place and you get
float = 0
but, unfortunately, a template value (not type) parameter can't be of type floating point, so you get an error.
If you use int instead of T, the substitution give you
int = 0
and this is correct.
Another solution can be use the following form
typename = std::enable_if_t<std::is_floating_point<T>::value, T>
as suggested by Andreas Loanjoe, because the substitution give you
typename = float
that is a valid syntax.
But this solution has the drawback that doesn't works when you want to write two alternative functions, as in the following example
// the following solution doesn't works
template <typename T,
typename = std::enable_if_t<true == std::is_floating_point<T>::value, int>>
void foo ()
{ }
template <typename T,
typename = std::enable_if_t<false == std::is_floating_point<T>::value, int>>
void foo ()
{ }
where works the solution based on the value
// the following works
template <typename T,
std::enable_if_t<true == std::is_floating_point<T>::value, int> = 0>
void foo ()
{ }
template <typename T,
std::enable_if_t<false == std::is_floating_point<T>::value, int> = 0>
void foo ()
{ }
I am trying to learn about SFINAE (i am following this tutorial), but there are some... "design choices" I do not understand and, as such, I find them confusing.
Let's assume I have a situation like this (included re-implementation of std::enable_if is there just to demonstrate how I understand enable_if)
// A default class (class type) declaration. Nothing unusual.
template <bool, typename T = void>
struct enable_if
{};
// A specialisation for <true, T> case. I understand 'why-s' of this.
// -- 'why-s': if I attempt to access 'enable_if<false, T>::type' (which does not exist) I will get a substitution failure and compiler will just "move-on" trying to match "other cases".
template <typename T>
struct enable_if<true, T> {
typedef T type;
};
// Here lies my problem:
template <class T,
typename std::enable_if<std::is_integral<T>::value,T>::type* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
(1) The very 1st thing I have a "problem" with, is bool literal (true/false). I understand they are correct and templates can accept compile-time constant values of primitive data types (plain-old-data types) but if I were tasked to design the enable_if "mechanisms" instead of using true/false I would create a tag classes true_t(or True) and false_t (or False) as follows:
class true_t {}; // or True
class false_t {}; // or False
template<typename T>
class is_integral // just to have "something" to use with "enable_if"
{
using result = false_t;
};
template<>
class is_integral<int32_t> // same with all other int types
{
using result = true_t;
};
template <typename B, typename T = void>
struct enable_if
{};
template <typename T>
struct enable_if<true_t, T>
{
using type = T;
};
(2) The second thing I find redundant is the need to specify typename T template parameter. Wouldn't it be easier / better to just implement enable_if as follows:
template <typename B>
struct enable_if
{};
template <>
struct enable_if<true_t>
{
using type = void; // the 'type' exists therefore substitution failure will not occur.
};
I am well aware that all my propositions are extremely inferior to the currently existing solutions, but I don't understand why... What portion of the functionality (important functionality) of current SFINAE did i shave off? (Not even realizing it...)
I know that, on this site, I am obligated to ask a single question within a... single "question-post-like" format, but if you find it acceptable could I also ask what will this syntax:
std::enable_if</* ... */>::type* = nullptr
accomplish? It's beyond my understanding right now...
The very 1st thing I have a "problem" with, is bool literal (true/false). I understand they are correct and templates can accept compile-time constant values of primitive data types (plain-old-data types) but if I were tasked to design the enable_if "mechanisms" instead of using true/false I would create a tag classes true_t(or True) and false_t (or False) as follows
The issue with use a tag type instead of just a bool is that you have to add extra complexity to the code. If you want to check a compile time condition, like sizeof for instance, you couldn't just do sizeof(T) == 8. You would have to make an abstraction that does the check and the returns the appropriate tag type.
The second thing I find redundant is the need to specify typename T template parameter. Wouldn't it be easier / better to just implement enable_if as follows
Not really. What if you want to use the SFINAE for the return type? You would only be able to have a void function then, which is unnecessarily limiting. Instead what you can do is use what was later added in C++14 and C++17 and make aliases. This makes the names non dependent and lets you drop the typename
template< bool B, class T = void >
using enable_if_t = typename enable_if<B,T>::type;
template< class T >
inline constexpr bool is_integral_v = is_integral<T>::value;
This allows you to rewrite
template <class T,
typename std::enable_if<std::is_integral<T>::value,T>::type* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
to
template <class T,
std::enable_if_t<std::is_integral_v<T>,T>* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
although I prefer to use a bool for the type of enable_if_t like
template <class T,
std::enable_if_t<std::is_integral_v<T>, bool> = true>
void do_stuff(T& t) { /* do stuff */ };
I know that, on this site, I am obligated to ask a single question within a... single "question-post-like" format, but if you find it acceptable could I also ask what will this syntax:
std::enable_if</* ... */>::type* = nullptr
accomplish?
It makes a pointer to the type that std::enable_if "returns" and sets it to null pointer. The goal here is to make a template parameter that will only exist if the condition is true. You could rewrite it to
typename = typename std::enable_if</* ... */>::type
so instead of having a non type parameter you have a type parameter. They both accomplish the same thing but the latter wont work with overloading the function for different enable_if's since default template parameters are not part of the signature. The first version which uses non type parameters is included in the function signature and does allow you to overload the enable_if's.
First off there exists tag-types for true and false, namely std::true_type and std::false_type.
Let's say we made the enable_if work with this instead of a bool parameter. You could then no longer do things like std::enable_if<1 == 1>::type since 1 == 1 evaluates to a bool. So does most things you will want to test here.
On the other hand, the existing tag types can be used in a enable_if since they contain a value and have a operator() that return said value.
So it seems to me that a lot of convenience would be lost in doing it your way, and from what I can see nothing would be gained.
For point 2, it's simply a convenience to be able to specify what type you want enable_if to hold if it's true. It defaults to void, but if you want you can easily have it deduce an int, double ect. which can be useful sometimes.
The short version (Read if you have no patience like me):
What does setting a typename template parameter to a default of 0 do in C++?
My enable if struct:
/**
* #brief Can be used to enable a template definition using a boolean value
*/
template<lfBool Condition>
struct lfEnableIf
{ };
template<>
struct lfEnableIf<true>
{
typedef lfInt Type;
};
My boolen constant struct:
template<lfBool Val>
struct lfBoolConstant
{
static const lfBool Value = Val;
};
typedef lfBoolConstant<true> lfTrueType;
typedef lfBoolConstant<false> lfFalseType;
My type trait struct (just one of the specializations):
template <typename NumT> struct lfIsArithmetic : lfFalseType{};
template <> struct lfIsArithmetic<lfChar> : lfTrueType{};
And finally my usage of it all:
template<typename T, typename lfEnableIf<lfIsArithmetic<T>::Value>::Type = 0>
struct Test
{
static void print()
{
std::cout << "OK!" << std::endl;
}
};
int main()
{
Test<lfFloat>::print();
Test<lfBool>::print();
return 0;
}
Sorry for the poor for formatting I am writing this on my cell phone.
The long version (Please read if you have the patience so you understand why there is not much code):
So, I am on vacation and don't have access to my workstation or laptop so I thought I would try out AIDE, which if you are unaware is an IDE for android that can compile C++. At home I am designing a game engine that includes Boost and I figured I would try to create something similar to the enable_if structures that are in the Boost.Core library. I got it mostly working but it would not compile unless I set the enable if structure in the template I was enabling to default to 0! This is what you do with the Boost enable_if and disable_if templates. So what does setting a typename template parameter to a default of 0 do in C++?
Thanks!
This line
template<typename T, typename lfEnableIf<lfIsArithmetic<T>::Value>::Type = 0>
declares a named template type parameter T and a nameless template value parameter of type lfEnableIf<lfIsArithmetic<T>::Value>::Type, i.e. the second parameter declarations is basically a more convoluted version of simple
template <int N = 0> struct S {};
The type lfEnableIf<lfIsArithmetic<T>::Value>::Type resolves into type lfInt when the enabling condition is met, meaning that the whole thing in such cases is equivalent to
template<typename T, lfInt = 0>
However, since type of that second template parameter is a nested type of a dependent template lfEnableIf, you are required to use the keyword typename to tell the compiler that member Type actually refers to a type and not to something else (i.e. to disambiguate the situation).
Again, the second parameter of the template is nameless, but you can give it a name, if you wish. It won't change anything
template<typename T, typename lfEnableIf<lfIsArithmetic<T>::Value>::Type V = 0>
In the above example I called it V. The name of that parameter is not used anywhere in the template, which is why there's no real need to specify it explicitly. It is a dummy parameter, which is also the reason it has a dummy default value (you can replace 0 with 42 - it won't change anything either).
In this case keyword typename creates a misleading similarity between the two parameter declarations of your template. In reality in these parameter declarations the keyword typename serves two very very very different unrelated purposes.
In the fist template parameter declaration - typename T - it declares T as a template type parameter. In this role keyword typename can be replaced with keyword class
template <class T, ...
In the second declaration - typename lfEnableIf<lfIsArithmetic<T>::Value>::Type = 0 - it serves a secondary purpose - it just tells the compiler that lfEnableIf<lfIsArithmetic<T>::Value>::Type is a type and thus turns the whole thing into a value parameter declaration. In this role keyword typename cannot be replaced with keyword class.
Say I have a template class like so:
template < typename TParam >
class Test
{
// content
};
I want to pull out the first template parameter of TParam if it's a specialization of a class template. Something like:
template < typename TParam >
class Test
{
using TParamInner = TemplateType<TParam>::Type;
// use TParamInner here
};
Additional info:
I have access to all of C++98.
I have access to a subset of C++11.
I would prefer to avoid the stdlib if possible (assume this is
because I'm using an embedded system for which no stdlib is available and/or because I am heavily memory-constrained)
You can get close with something like:
template <class >
struct first_template_param;
template <template <class...> class Z, class T, class... Ts>
struct first_template_param<Z<T, Ts...>> {
using type = T;
}
It won't handle std::array or any other class templates that take non-type template parameters. But it'll handle all the "normal" class templates. You can always then add extra specializations for all the ones you want:
template <class T, size_t N>
struct first_template_param<std::array<T,N>> {
using type = T;
}
Thanks to #Barry for spurring the solution along.
It's not a complete answer for all template types, but it works for templates where all parameters are types, which is a large number of the most useful templates.
template < typename Head, typename ... Tail >
struct split { using first = Head; };
template <class >
struct cls_template_info; // fails on non-templates
template <template <class...> class Z, class... Ts>
struct cls_template_info<Z<Ts...>>
{
using type = typename split<Ts...>::first; // typename used to disambiguate
};
This can then be used as using T = cls_template_info<std::vector<int>>::first;.
You can't. A template type is never carried up to runtime. You have to instantiate it (this leads to a complete new type), and the compiler then generates the needed code to make it appear as if you have defined specifically for the type parameters you specified. Indeed, in old compilers (this has been solved a lot of time ago) when you instantiate a generic type in several compilation units, that lead to several repetitions of the same code in the final program. But as I've said, this has been solved time ago.
Hello guys (and happy new year!)
I'm writing a (not really) simple project in C++ (my first, coming from plain C). I was wondering if there is a way to simplify the definition for multiple functions having the same template pattern. I think an example would be better to explain the problem.
The context
Let's assume I have a "Set" class which represents a list of numbers, defined as
template <class T>
class Set {
static_assert(std::is_arithmetic<T>(), "Template argument must be an arithmetic type.");
T *_address;
...
}
So if I have an instance (say Set<double>) and an array U array[N], where U is another arithmetic type and N is an integer, I would like to be able to perform some operations such as assigning the values of the array to those of the Set. Thus I create the function template inside the class
template <class U, int N>
void assign(U (&s)[N]) {
static_assert(std::is_arithmetic<U>(), "Template argument must be an arithmetic type.");
errlog(E_BAD_ARRAY_SIZE, N == _size);
idx_t i = 0;
do {
_address[i] = value[i];
} while (++i < size);
}
The problem
As far as my tests went the code above works perfectly fine. However I find it REALLY REALLY ugly to see since I need the static_assert to ensure that only arithmetic types are taken as arguments (parameter U) and I need a way to be sure about the array size (parameter N). Also, I'm not done with the assign function but I need so many other functions such as add, multiply, scalar_product etc. etc.!
The first solution
I was wondering then if there is a prettier way to write this kind of class. After some work I've come up with a preprocessor directive:
#define arithmetic_v(_U_, _N_, _DECL_, ...) \
template <class U, idx_t N> _DECL_ \
{ \
static_assert(std::is_arithmetic<U>(),"Rvalue is not an arithmetic type."); \
errlog(E_BAD_ARRAY_SIZE, N == _size); \
__VA_ARGS__ \
}
thus defining my function as
arithmetic_v(U, N,
void assign(U (&value)[N]),
idx_t i = 0;
do {
_address[i] = value[i];
} while (++i < _size);
)
This is somehow cleaner but still isn't the best since I'm forced to lose the brackets wrapping the function's body (having to include the static_assert INSIDE the function itself for the template parameter U to be in scope).
The question
The solution I've found seems to work pretty well and the code is much more readable than before, but... Can't I use another construct allowing me to build an even cleaner definition of all the functions and still preserving the static_assert piece and the info about the array size? It would be really ugly to repeat the template code once for each function I need...
The thanks
I'm just trying to learn about the language, thus ANY additional information about this argument will be really appreciated. I've searched as much as I could stand but couldn't find anything (maybe I just couldn't think of the appropriate keywords to ask Google in order to find something relevant). Thanks in advance for your help, and happy new year to you all!
Gianluca
I strongly suggest against using macros, unless there is no way around them (a case I could think of is getting the line number for debugging purposes). From the Google C++ Style Guide (http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Preprocessor_Macros):
Macros mean that the code you see is not the same as the code the compiler sees. This can introduce unexpected behavior, especially since macros have global scope.
I really don't see why you consider using a static_assert ugly. There is another way to ensure that a template is specialized only for some types using SFINAE.
template <class T, class Enable = void>
class X;
template <class T>
class X<T, typename std::enable_if<std::is_integral<T>::value>::type> {
};
and you could do this even prettier using a using statement (no pun intended):
template <class T>
using enable_if_integral_t = typename std::enable_if<std::is_integral<T>::value>::type;
template <class T, class Enable = void>
class X;
template <class T>
class X<T, enable_if_integral_t<T>> {
};
And now
X<int> x; // ok, int is integral
X<float> y; // compile error
SFINAE (Substitution failure is not an error) is a feature in C++ in which you don't get an error if a template specialization fails.
template <bool Cond, class T = void> struct enable_if. The type T is enabled as member type enable_if::type if Cond is true. Otherwise, enable_if::type is not defined. So for a float type is_integral is false and enable_if::type doesn't exist, so the template specialization
template <class T>
class X<T, typename std::enable_if<std::is_integral<T>::value>::type>
fails, but the generic template is used instead
template <class T, class Enable = void>
class X;
which is declared, but not defined.
This is useful as you can have more specializations like:
template <class T>
using enable_if_integral_t = typename std::enable_if<std::is_integral<T>::value>::type;
template <class T>
using enable_if_floating_t = typename std::enable_if<std::is_floating_point<T>::value>::type;
template <class T, class Enable = void>
class X;
template <class T>
class X<T, enable_if_integral_t<T>> {
};
template <class T>
class X<T, enable_if_floating_t<T>> {
};
Hope you find this at least interesting.
Happy new year!
Edit
Where should I put the <T, enable_if_integral_t<T>> in a function definition? I can only get this done with class templates...
For a function, the enable_if::type can be the return type. For example if f returns int, you can have:
#include <type_traits>
template <class T>
typename std::enable_if<std::is_integral<T>::value, int>::type f(T a) {
return 2 * a;
}
int main() {
f(3); // OK
f(3.4); // error
return 0;
}
and with using:
#include <type_traits>
template <class T, class Return = void>
using enable_if_integral_t = typename std::enable_if<std::is_integral<T>::value, Return>::type;
template <class T>
enable_if_integral_t<T, int> f(T a) {
return 2 * a;
}
int main() {
f(3); // OK
f(3.4); // Error
return 0;
}
I don't understand why you consider the static_assert or errlog statements so ugly, and suspect it's partly unfamiliarity with the language. Still, you could easily write a function or macro (if you want to use __LINE__ inside the assign etc. function), to move them out of line, allowing usage like:
template <class U, int N>
void assign(U (&s)[N]) {
assert_array_n_numbers(s);
idx_t i = 0;
do {
_address[i] = s[i];
} while (++i < size);
}
Can't I use another construct allowing me to build an even cleaner definition of all the functions and still preserving the static_assert piece and the info about the array size? It would be really ugly to repeat the template code once for each function I need...
In terms of what's possible - though IMHO likely undesirable obfuscation - you could have your functions accept (an) argument(s) that has a templated implicit constructor from an array, verifying it's arithmetic in that constructor then verifying size in the function using it, allowing usage like:
template <typename U>
void assign(Arithmetic_Array<U>& s) {
assert_same_size(s);
idx_t i = 0;
do {
_address[i] = s[i];
} while (++i < size);
}
Implementation:
template <typename T>
class Arithmetic_Array
{
public:
template <size_t N>
Arithmetic_Array(T (&a)[N])
: p_(&a), size_(N)
{
static_assert(std::is_arithmetic<T>(),"Rvalue is not an arithmetic type.");
}
T& operator[](size_t i) { return p_[i]; }
const T& operator[](size_t i) const { return p_[i]; }
size_t size() const { return size_; }
private:
T* p_;
size_t size_;
};
Discussion
"cleaner" can be subjective. In particular, you should consider the value of "normal" non-macro using-the-intuitive-type C++ source as documentation and for maintainability. If a macro substantially simplifies many functions - and particularly if it's only used in an implementation file and not a shared header - then it's worthwhile, but if there's only marginal benefit it's not worth the obfuscation and de-localisation. All that template stuff might seem convoluted and ugly when you're new to the language, but after a while it's understood at a glance and helps readers understand what the function goes on to do.
It's also common in C++ to embrace a "duck typing" attitude to template's parametric polymorphism. That means that you can let people pass in arguments of whatever type, and if those types support the operations that the template implementation attempts on them (i.e. compile), then hopefully it'll be what the caller wants. That's one reason that it's a good idea to create types that have predictable semantic behaviour, for example - only using operator overloading when the affect is similar to the same operators on built in types or std::string.
The stricter enforcement you'd like has its place though - Bjarne Stroustrup and others have spent a lot of time working on "Concepts" which are a mechanism for enforcing expectations on types used as template parameters, and would have been a good fit for your "arithmetic types" stipulation here. I hope they'll make it into the next C++ Standard. Meanwhile, static assertions are a good way to go.