Reputation:
I was playing around with templates, and I wanted to use them to write a recursive function that will be evaluated during compile-time. And I want it to branch depending on the number that I pass to it. The function does have a constraint; I want to keep the return value.
So this was my attempt to write the function (it doesn't compile):
template<int n, typename std::enable_if_t<n==1>>
constexpr auto fun() { return std::make_tuple(1); }
template<int n, typename std::enable_if_t<n==2>>
constexpr auto fun() { return std::make_tuple(fun<1>(), 2); }
template<int n, typename enable = void>
constexpr auto fun() {
return std::tuple_cat(fun<n-1>(), n);
}
int main() {
constexpr auto x = fun<4>();
return 0;
}
The issue that I'm facing is that I'm not sure where to put the std::enable_if_t
statement, and how to write it exactly to assure that my function branches correctly. What am I missing here?
Upvotes: 0
Views: 224
Reputation: 9835
Assuming you want to concatenate your tuple and create it in the form of
fun<4>() == tuple(1, 2, 3, 4);
you can write two templates like
template<int n, std::enable_if_t<n == 1>* = nullptr>
constexpr auto fun()
{
return std::make_tuple(1);
}
template<int n, std::enable_if_t<n != 1>* = nullptr>
constexpr auto fun()
{
return std::tuple_cat(fun<n-1>(), std::tuple(n));
}
However, that's not the "C++17 way" of doing this. It can be expressed much nicer by using if constexpr
template<int n>
constexpr auto fun2()
{
if constexpr (n > 1)
return std::tuple_cat(fun2<n-1>(), std::tuple(n));
else
return std::tuple(1);
}
Regarding sequence creations (things like 1, 2, 3, 4, ...
) there is also std::integer_sequence
which can be used in combination with template parameter packs
template <int... nums>
constexpr auto construct(std::integer_sequence<int, nums...>)
{
return std::tuple((nums + 1)...);
}
template<int n>
constexpr auto fun3()
{
return construct(std::make_integer_sequence<int, n>());
}
Here is a full example.
Upvotes: 2
Reputation: 66240
Not clear for me what do you exactly want but... let me guess: you want something as follows
#include <tuple>
#include <type_traits>
template<int n, std::enable_if_t<n==1, bool> = true>
constexpr auto fun() { return std::make_tuple(1); }
template<int n, std::enable_if_t<n!=1, bool> = true>
constexpr auto fun() { return std::tuple_cat(fun<n-1>(), std::make_tuple(n)); }
int main() { constexpr auto x = fun<4>(); }
The trick
template<int n, typename enable = void>
is a SFINAE trick for template class, where there is the main version and some specialization; so the main version define the signature and the specializations a SFINAE enabled/disabled through std::enable_if
.
With functions you have overloading of functions but not partial specialization. You have to enable/disable every single overloaded function indipendently
template<int n, std::enable_if_t<n==1, bool> = true>
// ...
template<int n, std::enable_if_t<n!=1, bool> = true>
// ...
Observe that I have written
template<int n, std::enable_if_t<n==1, bool> = true>
and not
template<int n, typename = std::enable_if_t<n==1>>
The second form also works to enable/disable a single function but doesn't works when you have different functions with the same signature (as in your case) and you want enable only one version.
Upvotes: 1