Alternative tuple iteration using constexpr - c++

I want to write a function which kind of iterates over a std::tuple<...>. The iteration itself won't produce any problems regarding the tuple's template types, because '...' have the same types (like int, int, int, ...). I implemented already a working function 'Foo' with a helper struct 'Helper' using template metapgrogramming – everything is fine.
But when I want to implement an alternative version, using a constexpr function 'helper', the compiler (g++ 5.2.0) gets stuck in an infinite loop of error messages. From what I can get from these messages, the 'position' template parameter is instantiated as ridicilously big (== 4294967245) instead of (== 1). I tried to get both versions as close in syntax and nomenclature as possible.
Minimal example
#include <tuple>
// template metaprogramming version
template
<class T, std::size_t position>
struct Helper{
static int
help(T tuple) {
return std::get<position>(tuple) +
Helper<T,position - 1>::help(tuple);
}
};
// template metaprogramming version, specialized
template
<class T>
struct Helper<T,0>{
static int
help(T tuple) {
return std::get<0>(tuple);
}
};
// function version, not working
template
<class T, std::size_t position>
constexpr int
helper(T tuple) {
return
0 == position ?
std::get<position>(tuple) + helper<T,position-1>(tuple) :
std::get<0>(tuple);
}
template
<class T>
auto
Foo(T tuple) {
constexpr std::size_t dimension = std::tuple_size<T>::value;
// working version, using the helper struct
return Helper<T,dimension - 1>::help(tuple);
// wrong(?) version, using the constexpr helper function
return helper<T,dimension - 1>(tuple);
}
int main() {
std::tuple<int,int> t(1,1);
Foo(t);
return 0;
}
My questions:
Is it principally wrong to try this kind of iteration during compile
time with constexpr functions?
If not, is it a bug in the compiler
or how should a correct version look like?
I am totally aware that, due to the same types in the tuple (int,int,...), one could implement a similar version with vectors. But I think that the tuple version is conceptually better to my kind of problem and faster during runtime.

When you have a function template - all the code must be compiled out. So here:
template
<class T, std::size_t position>
constexpr int helper(T tuple) {
return
0 == position ?
std::get<position>(tuple) + helper<T,position-1>(tuple) :
std::get<0>(tuple);
}
We always compile both parts of the conditional (side-note: your conditional is backwards). So when position == 0, both the std::get<0>(tuple) part is compiled and the std::get<0>(tuple) + helper<T, -1>(tuple) part is compiled. But then to do that, we'd need to compile helper<T, -2>(tuple). And helper<T, -3>(tuple). We recursive infinitely.
Your specialization approach works because Helper<T, 0> just does std::get<0>. There is no other logic in there, so we stop. If you want to do this functionally, an easier way would be to pass the position as an argument. That is:
template <std::size_t position>
using Pos = std::integral_constant<std::size_t, position>; // to save me some typing
template <typename T>
constexpr int helper(T const& tuple, Pos<0> )
{
return std::get<0>(tuple);
}
template <typename T, std::size_t position>
constexpr int helper(T const& tuple, Pos<position> )
{
return std::get<position>(tuple) + helper(tuple, Pos<position - 1>{});
}
Here, we do the conditional via overloading - so once we get to helper(T, Pos<0> ), we successfully terminate the recursion.
In C++1z, this becomes a ton easier with fold expressions, where you can just do:
template <typename T>
constexpr int sum_tuple(T const& tuple) {
return sum_tuple(tuple, std::make_index_sequence<std::tuple_size<T>::value>{});
}
template <typename T, std::size_t... Is>
constexpr int sum_tuple(T const& tuple, std::index_sequence<Is...> )
{
return (std::get<Is>(tuple) + ... );
}

So my questions: Is it principally wrong to try this kind of iteration during compile time with constexpr functions?
Yes, it's wrong, as you've done it. It can be done, but you can't terminate the compile-time recursion with a run-time condition.
When you instantiate helper<T, N>(T) that instantiates helper<T, N-1>(T) which instantiates herlper<T, N-2>(t) and so on, with nothing to terminate the recursion.
The class template version terminates when it reaches N==0 because of the partial specialization, but you can't partially specialize a function template.
Barry's solution works by using a second overload to terminate the recursion, so that when it reaches 0 it chooses a different function and doesn't keep instantiating the same one forever.

Related

C++20 requires expression does not catch static_assert

I was really excited when I first heard about C++20 constraints and concepts, and so far I've been having a lot of fun testing them out. Recently, I wanted to see if it's possible to use C++20 concepts to test the constraints of classes or functions. For example:
template <int N>
requires (N > 0)
class MyArray { ... };
template <int N>
concept my_array_compiles = requires {
typename MyArray<N>;
};
my_array_compiles<1>; // true
my_array_compiles<0>; // false
At first I didn't have any issues, but I encountered a case where static_assert in a dependent function prevents compilation, even though it appears in a requires expression. Here is an example that illustrates this:
template <bool b>
requires b
struct TestA {
void foo() {}
};
template <bool b>
struct TestB {
static_assert(b);
void foo() {}
};
template <template<bool> class T, bool b>
concept can_foo = requires (T<b> test) {
test.foo();
};
can_foo<TestA, true>; // true
can_foo<TestA, false>; // false
can_foo<TestB, true>; // true
// can_foo<TestB, false>; does not compile
TestA and TestB should work similarly for most use cases (although I found that TestB<false> can be used as a type as long as it isn't instantiated or dereferenced). However, my expectation was that a failed static_assert within a requires expression would cause it to evaluate to false instead. This is especially important for using library code that still uses static_assert. For example, std::tuple_element:
template <class T>
concept has_element_0 = requires {
typename tuple_element_t<0, T>;
};
has_element_0<tuple<int>>; // true
// has_element_0<tuple<>>; does not compile
When I pass in an empty tuple to the above concept, I get the error static_assert failed due to requirement '0UL < sizeof...(_Types)' "tuple_element index out of range". I've tested this on g++ 10.3.0 and clang 12.0.5. I was able to work around this issue by providing a wrapper that uses constraints, but it somewhat defeats the purpose since I am essentially preventing the compiler from seeing the static_assert by enforcing the same condition at a higher level.
template <size_t I, class T>
requires (I >= 0) && (I < tuple_size_v<T>)
using Type = tuple_element_t<I, T>;
template <class T>
concept has_element_0 = requires {
typename Type<0, T>;
};
has_element_0<tuple<int>>; // true
has_element_0<tuple<>>; // false
And it doesn't always work depending on how std::tuple_element is used:
template <size_t I, class T>
requires (I >= 0) && (I < tuple_size_v<T>)
tuple_element_t<I, T> myGet(const T& tup) {
return get<I>(tup);
}
template <class T>
concept has_element_0 = requires (T tup) {
myGet<0>(tup);
};
has_element_0<tuple<int>>; // true
// has_element_0<tuple<>>; does not compile
So ultimately my questions are: is this expected behavior that requires expressions don't take static_assert into account? If so, what was the reason for that design? And finally, is there a better way to accomplish my goal on classes with static_assert without using the above workaround?
Thanks for reading.
Yes, nothing in the content of the stuff you interact with is checked. Just the immediate context of the declaration.
In some cases with decltype the non immediate context of some constructs is checked, but any errors remain hard.
This was done (way back) to reduce the requirements on compilers. Only in what is known as "immediate context" do the compilers need to be able to cleanly back out when they see an error and continue compiling.
Static assert is never suitable for this purpose. Static assert, if hit, ends the compilation.
If you want to avoid the static assert (that is expected to end compilation) then you need to provide an alternative.
Once the concept is designed, create a variant for the not (!) of that concept:
#include <tuple>
#include <variant>
template <std::size_t I, class T>
requires (I >= 0) && (I < std::tuple_size_v<T>)
using Type = std::tuple_element_t<I, T>;
template <class T>
concept has_element_0 = requires {
typename Type<0, T>;
};
bool test1()
{
return has_element_0<std::tuple<int>>; // true
}
bool test2()
{
return has_element_0<std::tuple<>>; // false
}
template <std::size_t I, class T>
requires (I >= 0) && (I < std::tuple_size_v<T>)
std::tuple_element_t<I, T> myGet_impl(const T& tup) {
return get<I>(tup);
}
template <class T>
concept alt_has_element_0 = requires (T tup) {
myGet_impl<0>(tup);
};
template <class T>
auto myGet0();
template <class T>
requires (alt_has_element_0<T>)
auto myGet0(const T& tup)
{
return myGet_impl<0, T>(tup);
}
auto test3()
{
std::tuple<int> X{7};
return myGet0(X); // true
}
template <class T>
requires (!alt_has_element_0<T>)
auto myGet0(const T& tup)
{
return std::monostate{};
}
auto test4()
{
std::tuple<> X;
return myGet0(X); // true
}
see it here;
Notice for test4() to compile, the code above defiles what to do if we do not fulfill the requirements of the concept. I stole std::monostate from variant for this.

What are the syntax and semantics of C++ templated code?

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 ()
{ }

How can I get access to this nested template parameter?

Here is my code:
template<
template <typename TSEvent,
typename ...TSEvents> typename V,
typename... Filtered>
constexpr auto filter() {
if constexpr(sizeof...(TSEvents) == 0) {
return type_list<Filtered...>{};
}
if constexpr(is_default_constructible<TSEvent>::value) {
return filter<<TSEvents...>, Filtered...>();
}
return filter<<TSEvents...>, Filtered...>();
}
I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?
Usually through another level of indirection, and usually a struct that we can specialize.
For example:
namespace detail
{
template<class...>
struct filter_t;
template<template<class, class...> class V, class TSEvent, class... TSEvents, class... Filtered>
struct filter_t<V<TSEvent,TSEvents...>, Filtered...>
{
static constexpr auto filter() {
return sizeof...(TSEvents);
}
};
} // detail
template<class... T>
constexpr auto filter()
{
return detail::filter_t<T...>::filter();
}
template<class T, class...U>
struct type_list{};
int main()
{
std::cout << filter<type_list<int, int, int>, int>();
}
Live Demo
Just to present another option, you could do this with only functions.
#include <iostream>
using namespace std;
template<typename...>
struct type_list{};
template < template <typename...> typename T,typename A,typename... B, typename... Filtered>
constexpr auto filter_impl(T<A,B...>*,type_list<Filtered...>)
{
using filtered_list = std::conditional_t<is_arithmetic<A>::value,
type_list<Filtered...,A>,
type_list<Filtered...>>;
if constexpr (sizeof...(B) == 0)
return filtered_list();
else
return filter_impl( (T<B...>*)0, filtered_list());
}
template <typename T>
constexpr auto filter()
{
return filter_impl( (T*)0,type_list<>());
}
struct not_arethmetic{};
int main() {
auto b = filter< type_list<not_arethmetic,int,bool,not_arethmetic,double> >();
static_assert(std::is_same< decltype(b) , type_list<int,bool,double>>::value);
return 0;
}
Demo
One thing, In your original example your first if expression will mean that the final TSEvent is not checked, as it returns if the varadic TSEvents... is zero size, but there will be one final element to check whether is_default_constructible.
Also, you might find this post useful regarding template template parameter names.
I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?
Short answer: no.
Long answer: with
template<
template <typename TSEvent,
typename ...TSEvents> typename V,
typename... Filtered>
constexpr auto filter()
you set two template arguments for the filter() function.
The first one, related to the TSEvents variadic list, is a template-template argument that receive one or more types argument.
But your function doesn't receive a type that is based over that template-template (with a fixed TSEvent type and a fixed TSEvents); receive the template-template.
So doesn't make sense the test size...(TSEvents) because, for filter() isn't
fixed the TSEvents list.
To explain this in another way... you can call filter this way
filter<std::tuple, short, int, long>();
Ask for sizeof...(TSEvents) is asking how many types contains std::tuple where std::tuple is only the container of types but without contained types.
If you want to make some sort of actions in your filter() function, you need a type template parameter, not a template-template parameter.
It's simpler with classes (see AndyG's answer) where you can use partial specialization (with functions you can't) or with function when they receive arguments from which you can deduce types.
Suppose your filter() receive an object of type V<SomeTypes...> and an object of type std::tuple<Filtered...>, you can write something as follows (caution: code not tested)
template<
template <typename ...> typename V,
typename TSEvent, typename ... TSEvents, typename... Filtered>
constexpr auto filter (V<TSEvent, TSEvents...> const & v,
std::tuple<Filtered...> const & t) {
/* some code where you can use also TSEvent and TSEvents... */
}
This way TSEvent and TSEvents... are deduced from the v argument.

Calling a member function if it exists, falling back to a free function and vice-versa

Can I write a template function taking an argument T that calls a member function foo if it exists on T, and if it doesn't calls a free function foo(T) instead (and fails to compile if neither exists)?
Something like:
template<typename T>
int call_foo(T t) {
// if T::foo() exists
return t.foo();
// else return foo(t);
}
How about the reverse case: preferring a free function foo before the member function? I cannot use any features introduced after C++11.
This isn't too hard. There are many methods of checking whether an arbitrary expression is valid. You can combine this with if constexpr in C++17 or tag dispatch earlier to get the behaviour you desire.
This uses C++17, but everything can be done in prior versions:
#include <type_traits>
#include <utility>
// This is just one way to write a type trait, it's not necessarily
// the best way. You could use the Detection Idiom, for example
// (http://en.cppreference.com/w/cpp/experimental/is_detected).
template <typename T, typename = void>
struct has_member_fn
: std::false_type
{};
// std::void_t is a C++17 library feature. It can be replaced
// with your own implementation of void_t, or often by making the
// decltype expression void, whether by casting or by comma operator
// (`decltype(expr, void())`)
template <typename T>
struct has_member_fn<T,
std::void_t<decltype(std::declval<T>().foo())>>
: std::true_type
{};
template <typename T, typename = void>
struct has_free_fn
: std::false_type
{};
template <typename T>
struct has_free_fn<T,
// Be wary of ADL. You're basically asking the compiler,
// "What's the result of foo(T{}) if I were to call that
// here?" That syntax can call functions via ADL
std::void_t<decltype(foo(std::declval<T>()))>>
: std::true_type
{};
template <typename T>
int call_foo(T t) {
// if constexpr is C++17, but you can use tag dispatch to
// do the same in prior versions
if constexpr (has_member_fn<T>::value) {
return t.foo();
} else {
// you could make this an `else if constexpr (has_free_fn<T>::value)`
// and provide a better case for if neither exists
return foo(t);
}
}
Live on Godbolt
Pre C++17 you can's compile/not compile different parts of the same function with if constexpr.
So, pre C++17, you have to do, somewhere, two different functions.
An example: if you prepare a couple of helper functions
template <typename T>
auto call_foo_h (T t, int) -> decltype( t.foo() )
{ return t.foo(); }
template <typename T>
auto call_foo_h (T t, long) -> decltype( foo(t) )
{ return foo(t); }
that are SFINAE enabled only if T::foo() exist (the first one) or if a free foo() exist (the second one), you can write call_foo() as follows
template <typename T>
int call_foo (T const & t)
{ return call_foo_h(t, 0); }
//......................^ a int value
Observe the second (unused) parameter in call_foo_h(); an int in the T::foo() version, a long in the free version.
Here is the trick: calling call_foo_h with an int (0) you call preferably the int version (the T::foo()), when available, and the long version otherwise.
How about the reverse case: preferring a free function foo before the member function?
In this case write call_foo() as follows
template <typename T>
int call_foo (T const & t)
{ return call_foo_h(t, 0L); }
//......................^^ a long value
That is: call call_foo_h with a long value, giving the precedence to the free foo() version.

Why is this variadic function ambiguous?

This is related to my earlier post. I'd like to know why one attempted solution didn't work.
template <typename... T> /* A */
size_t num_args ();
template <>
size_t num_args <> ()
{
return 0;
}
template <typename H, typename... T> /* B */
size_t num_args ()
{
return 1 + num_args <T...> ();
}
If I try to call, say, num_args<int,float>() then the error is that the function call is ambiguous:
A with T={int,float}
B with H=int, T={float}
I don't understand how this is ambiguous -- A is a declaration and B is a definition of the function declared by A. Right?
I'm trying to make this example work and the responses to my earlier question seem to claim that it can never work.
If that's the case, what's the point of variadic free functions? What can they do?
I don't understand how this is ambiguous -- A is a declaration and B
is a definition of the function declared by A. Right?
No. A is a declaration of a function template, and B is a declaration (and definition) of another function template.
The compiler has no way to decide between the two: they both have no arguments, and the template arguments are a match for both.
The one in the middle is an explicit total specialization of the function template declared in A.
If you tried to make B another specialization of A:
template <typename H, typename... T> /* B */
size_t num_args<H, T...>()
{
return 1 + num_args <T...> ();
}
... you'd end up with a partial specialization of a function template, which is not allowed.
You can do this with the usual trick of using a class template with partial specializations and a function template that calls into the class template:
template <typename... T>
class Num_Args;
template <>
struct Num_Args <>
{
static constexpr size_t calculate() {
return 0;
}
};
template <typename H, typename... T>
struct Num_Args <H, T...>
{
static constexpr size_t calculate() {
return 1 + Num_Args<T...>::calculate();
}
};
template <typename... T> /* B */
constexpr size_t num_args ()
{
return Num_Args<T...>::calculate();
}
Apropos the usefulness/uselessness of free variadic function templates: the usual use case for these is to have a variadic function parameter list, in which case a regular overload for the empty case will do just fine:
size_t num_args()
{
return 0;
}
template <typename H, typename... T> /* B */
size_t num_args (H h, T... t)
{
return 1 + num_args(t...);
}
EDIT:
As far as I can see, the following abuse of enable_if ought to work as a solution to your original question:
#include <utility>
// Only select this overload in the empty case
template <typename... T>
typename std::enable_if<(sizeof...(T) == 0), size_t>::type
num_args()
{
return 0;
}
template <typename H, typename... T>
size_t
num_args()
{
return 1 + num_args<T...>();
}
(Edit2: Reversed the order of the overloads to make the code actually compile)
While I really like the std::enable_if<sizeof...(T) == 0, _> hack by JohannesD, I'll still drop the below hack that I don't actually remember where I learned from, which resolves the ambiguity with only free functions without having to use classes:
template <typename One>
size_t num_args() {
return 1;
}
template <typename First, typename Next, typename... Rest>
size_t num_args() {
return 1 + num_args<Next, Rest...>();
}
No partial specialization required, and it totally avoids the zero parameter case by ending the unpacking recursion at one parameter. Also, see https://stackoverflow.com/a/58295007/1525238 for an even nicer c++17 solution that uses the same idea.
The only catch is that it actually doesn't cover the zero template parameter case, which is impossible in the context of free functions AFAIK. If that is required from the consumer's perspective, a partially specialized class must be used, see R. Martinho Fernandes' answer.
As for the utility of variadic template free functions, I find them particularly useful as constexpr type-safe accumulating calculator utilities.