Ambiguous call to variadic template function with no parameters? - c++

When running this:
template <typename T>
struct CodeByType
{
static const int32_t Value = 7;
};
template <>
struct CodeByType<int>
{
static const int32_t Value = 1;
};
template <typename Arg, typename... Args>
int32_t Sum()
{
// The compiler complains on this line
return Sum<Arg>() + Sum<Args...>();
}
template <typename Arg>
int32_t Sum()
{
return CodeByType<Arg>::Value;
}
int main()
{
auto sum = Sum<int, char, double>();
}
I'm getting:
Error C2668 'Sum': ambiguous call to overloaded function
Can someone please explain why and how to overcome it?
This looks awfully similar to the below code, which does compile, so I suppose it has something to do with Sum not accepting any actual parameters.
template <typename T>
T adder(T first) {
return first;
}
template<typename T, typename... Args>
T adder(T first, Args... rest) {
return first + adder(rest...);
}
int main()
{
auto sum = adder(1, 7);
}

If you reduce your code to just:
Sum<int>();
You get a more helpful error message:
31 : <source>:31:16: error: call to 'Sum' is ambiguous
auto sum = Sum<int>();
^~~~~~~~
17 : <source>:17:9: note: candidate function [with Arg = int, Args = <>]
int32_t Sum()
^
24 : <source>:24:9: note: candidate function [with Arg = int]
int32_t Sum()
^
1 error generated.
So it is clearer that there is an overload ambiguity between the first overload with Args = <> and the second one. Both are viable.
One would might think as specialization for a solution:
template <typename Arg>
int32_t Sum<Arg>()
{
return CodeByType<Arg>::Value;
}
which would indeed solve the issue, had it been allowed by the standard. Partial function specializations are not allowed.
C++17 solution:
This is the most elegant solution:
constexpr if to the rescue:
template <typename Arg, typename... Args>
int32_t Sum()
{
if constexpr(sizeof...(Args) == 0)
return CodeByType<Arg>::Value;
else
return Sum<Arg>() + Sum<Args...>();
}
C++14 solution
We use SFINAE to enable/disable the function we want. Please note the function definition order had to be reversed.
template <typename Arg, typename... Args>
auto Sum() -> std::enable_if_t<(sizeof...(Args) == 0), int32_t>
{
return CodeByType<Arg>::Value;
}
template <typename Arg, typename... Args>
auto Sum() -> std::enable_if_t<(sizeof...(Args) > 0), int32_t>
{
return Sum<Arg>() + Sum<Args...>();
}
C++11 solution
just replace std::enable_if_t<> with typename std::enable_if<>::type

In c++17, it would simply be
template <typename... Args>
int32_t Sum()
{
return (CodeByType<Args>::Value + ...); // Fold expression
}
In C++11, you may do:
template <typename... Args>
int32_t Sum()
{
int32_t res = 0;
const int32_t dummy[] = {0, (res += CodeByType<Args>::Value)...};
static_cast<void>(dummy); silent warning about unused variable
return res;
}

My memories of the template mechanism are old but if I recall correctly, their information is erased at a certain point in the compilation process.
My guess is that in the second case, the functions get distinguished not by the difference in the template types, but by the difference in the arguments.
In your case, you have no arguments, so stripped of the template information the two overloaded versions are equal and it cannot distinguish between them when you call it.

Related

Specifying parameter pack length with concepts [duplicate]

Why I get a compilation error if uncomment line #1 and comment line #2?
Demo: https://godbolt.org/z/KW6dhsrKd
#include <utility>
template <typename, std::size_t> concept prefix = true;
template<std::size_t>
struct dummy { template<typename T> constexpr dummy(T){}; };
template <auto N>
consteval auto nth_element(auto... args)
{
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
//return [](prefix<Is> auto..., auto arg, auto...) { //compile error // #1
return [](dummy<Is> ..., auto arg, auto...) { // OK // #2
return arg;
}(args...);
}(std::make_index_sequence<N>());
}
int main()
{
static_assert(nth_element<0>(1, 2, 3) == 1);
static_assert(nth_element<1>(1, 2, 3) == 2);
static_assert(nth_element<2>(1, 2, 3) == 3);
return 0;
}
From [dcl.fct]/22:
An abbreviated function template is equivalent to a function template ([temp.fct]) whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance. [..] The invented type template-parameter is a template parameter pack if the corresponding parameter-declaration declares a function parameter pack.
Note that prefix<Is> auto... is both a pack and a pack expansion simultaneously.
So the transformed template would look like
[] <prefix<Is>... Args> (Args..., auto arg, auto...) {
return arg;
}(args...)
Which is equivalent to
[] <typename... Args> requires(prefix<Args, Is> && ...) (Args..., auto arg, auto...) {
return arg;
}(args...)
(This still generates the same error message, too.)
Where we can see that 1) Args is in a non-deduced context and will always remain empty, and 2) Is and Args would have to agree in length for the constraint to be satisfiable.
The solution given in the comments is not ideal IMO, as creating a tuple is likely relatively expensive (especially at compile time). I think your solution + proper forwarding is the best for now (until we get proper pack subscripts). I.e. something like this:
template<std::size_t>
struct dummy { constexpr dummy(auto&&){}; };
template <std::size_t N, typename... Args>
constexpr decltype(auto) nth_element(Args&&... args) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) -> decltype(auto) {
return [](dummy<Is> ..., auto&& arg, auto&&...) -> decltype(auto) { // OK // #2
return std::forward<decltype(arg)>(arg);
}(std::forward<Args>(args)...);
}(std::make_index_sequence<N>());
}

Creating a base case for Variadic Template recursion with no template arguments

I'm trying to use recursion with variadic templates. I would like the base case to have zero template arguments. After looking through stackoverflow answers to previous questions, I have found two kinds of responses to this problem:
You should not specialize templates functions. Herb Sutter wrote about that here: http://www.gotw.ca/publications/mill17.htm
You use template <typename = void> or template <typename T = void> . For example, the first answer here: How to write a variadic template recursive function?
I attempted to use the solution (2) in my problem, but received errors. This is a Minimal, Reproducible Example:
#include <iostream>
template<typename = void> // base case
int NumArguments() {
return 0;
}
template<typename FirstArg, typename... RemainingArgs>
int NumArguments() {
return 1 + NumArguments<RemainingArgs...>();
}
class A {
public:
A() {}
};
int main() {
std::cout << NumArguments<A>();
return 0;
}
Compilation in Microsoft Visual C++20 gave the errors:
error C2668: 'NumArguments': ambiguous call to overloaded function
message : could be 'int NumArguments<A,>(void)'
message : or 'int NumArguments<A>(void)'
message : while trying to match the argument list '()'
What does this error message mean? How do I create a zero-argument base case for recursion with variadic templates?
Edit: There were requests in the comments for a more complete description of my problem. The question really is the question title, and not "how do I get my code to work?", but I have not yet gotten my code to compile, so I have decided to share it.
NumArguments is a stand-in for another function ComputeSize that takes as input Args and returns an std::size_t.
template<typename = void>
constexpr std::size_t ComputeSize() {
return 0;
}
template<typename FirstArg, typename... RemainingArgs>
constexpr std::size_t ComputeSize() {
return FuncReturnSize<FirstArg>() + ComputeSize<RemainingArgs...>();
}
The possible list of Args in Args is finite and known prior to compilation. FuncReturnSize is overloaded for each of these Args. For example, two possible "overloads"(?) are
template <typename T>
requires ((requires (T t) { { t.Func()} -> std::same_as<double>; }) || (requires (T t) { { t.Func() } -> std::same_as<std::vector<double>>; }))
constexpr std::size_t FuncReturnSize() {
return 1;
}
template <typename T>
requires requires (T t) { { t.Func() } -> is_std_array_concept<>; }
constexpr std::size_t FuncReturnSize() {
return std::tuple_size_v<decltype(std::declval<T&>().Func())>;
}
The concept is_std_array_concept<> should check if the return value of t.Func() is some size array. I am not yet sure if it works. It is defined by
template<class T>
struct is_std_array : std::false_type {};
template<class T, std::size_t N>
struct is_std_array<std::array<T, N>> : std::true_type {};
template<class T>
struct is_std_array<T const> : is_std_array<T> {};
template<class T>
struct is_std_array<T volatile> : is_std_array<T> {};
template<class T>
struct is_std_array<T volatile const> : is_std_array<T> {};
template<typename T>
concept is_std_array_concept = is_std_array<T>::value;
I want all of this computation to be done at compile-time, so I have defined
template<std::size_t N>
std::size_t CompilerCompute() {
return N;
}
I should now be able to ComputeSize at compile time like so:
CompilerCompute<ComputeSize<Args...>()>()
The error message means exactly what it says, the call is ambiguous.
template<typename = void> // base case
constexpr int NumArguments() {
return 0;
}
This is not a template function that takes 0 arguments, this is a template function that takes one argument that's defaulted (so if the argument isn't specified, it's void). This means that NumArguments<A>() is a perfectly valid call to this function.
But, NumArguments<A>() is also a perfectly valid call to the variadic overload with an empty variadic pack (the NumArguments<A,>() overload listed in the error message).
What sets your case apart from the linked example is that in the linked example, the variadiac overload is templated on ints, not on types, so there's no ambiguity there. I've copied that implementation here:
template<class none = void>
constexpr int f()
{
return 0;
}
template<int First, int... Rest>
constexpr int f()
{
return First + f<Rest...>();
}
int main()
{
f<1, 2, 3>();
return 0;
}
Notice, the second overload of f is a variadic template where each template parameter must be an int value. Calling f<A>() won't match that overload if A is a type, so the ambiguity is avoided.
It's not possible to declare a zero-argument template function, so you're out of luck there. However, you can instead convert this to a class template as class templates can be partially specialized.
template <class ...Args>
struct NumArguments;
template <>
struct NumArguments<> {
static constexpr int value = 0;
};
template <class T, class ...Args>
struct NumArguments<T, Args...> {
static constexpr int value = 1 + NumArguments<Args...>::value;
};
This specific implementation could, of course, by simplified to use sizeof..., but the OP has indicated that their real use case is more complicated.
Here's another solution (without specialization), which uses a C++20 requires clause to resolve the ambiguity:
template <typename... Args> requires (sizeof...(Args) == 0)
constexpr int NumArguments() {
return 0;
}
template<typename FirstArg, typename... RemainingArgs>
constexpr int NumArguments() {
return 1 + NumArguments<RemainingArgs...>();
}
Example:
int main() {
std::cout << NumArguments<int>() << std::endl;
std::cout << NumArguments() << std::endl;
std::cout << NumArguments<float, int, double, char>() << std::endl;
return 0;
}
1
0
4
EDIT:
My old suggestion using concepts was incorrect. There's a good post here on using concepts and parameter packs.
You should guarantee the end of your variadic template without overloading the function.
A solution compiling with c++ standard 17 (in Microsoft Visual /std:c++17) is the following:
#include <iostream>
//Remove or comment base case!
template<typename FirstArg=void, typename... RemainingArgs>
constexpr int NumArguments() {
if (sizeof...(RemainingArgs) == 0)
return 1;
else
return (NumArguments<FirstArg>() + NumArguments<RemainingArgs...>());
}
class A {
public:
A() {}
};
int main() {
std::cout << NumArguments<A>();
return 0;
}
Sadly I couldn't quite get a is_std_array concept to work, but in terms of your NumArguments<T...>(), it could be done with fold expression pretty easily:
template<typename ...T>
int NumArguments()
{
return (FuncReturnSize<T>() + ...);
}
The fold expression here will be expanded like:
return (((FuncReturnSize<T1>() + FuncReturnSize<T2>()) + FuncReturnSize<T3>()) + FuncReturnSize<T4>)
Demo
Here I specialized std::integral and std::floating_point version of FuncReturnSize(), and any other types would just return sizeof(T). And you should be able to easily specialize other types with a good concept defined.
Note I also made FuncReturnSize()s consteval.

How to do variadic deduction in std::function's parameters?

I try to implement a function f: (std::function -> int) which will pass 1s into input_functor
with c++ variadic template.
Let input_functor be g.
For example:
If g is std::function<int(int,int)>, then f return g(1, 1).
If g is std::function<int(int,int,int)>, then f return g(1, 1, 1).
If g is std::function<int(int,int,int,int)>, then f return g(1, 1, 1, 1).
#include <functional>
#include <iostream>
template <typename T, typename... Args>
int apply(std::function<int(T, Args...)> func) {
auto tmp = [func](Args... args) {
return func(1, args...);
};
return apply(tmp);
}
template <typename T>
int apply(std::function<int(T)> func) {
return func(1);
}
int main() {
std::function<int(int, int)> f = [](int a, int b) {
return a + b;
};
std::cout << apply(f) << "\n";
return 0;
}
The compiler (clang++) error msg is that it cannot match candidates.
main.cpp:9:12: error: no matching function for call to 'apply'
return apply(tmp);
^~~~~
main.cpp:21:18: note: in instantiation of function template specialization 'apply<int, int>' requested here
std::cout << apply(f) << "\n";
^
main.cpp:5:5: note: candidate template ignored: could not match 'function<int (type-parameter-0-0, type-parameter-0-1...)>' against
'(lambda at main.cpp:6:16)'
int apply(std::function<int(T, Args...)> func) {
^
main.cpp:13:5: note: candidate template ignored: could not match 'function<int (type-parameter-0-0)>' against '(lambda at main.cpp:6:16)'
int apply(std::function<int(T)> func) {
^
1 error generated.
You have 2 issues:
definition order:
template <typename T>
int apply(std::function<int(T)> func) {
return func(1);
}
should be place before the recursive function to allow to be visible and ends recursion.
lambda is not a std::function, so deduction don't happen
template <typename T, typename... Args>
int apply(std::function<int(T, Args...)> func) {
auto tmp = std::function{[func](Args... args) { // C++17 CTAD
return func(1, args...);
}};
return apply(tmp);
}
Demo C++17
As you are limited to C++11, you might create traits to know which std::function is needed:
template <typenate T, typename Discarded>
struct always_first
{
using type = T;
};
template <typenate T, typename Discarded> using always_first_t = typename always_first<T, Discarded>::type;
// possibly directly
// template <typenate T, typename Discarded> using always_first_t = T;
// but old compilers might have some issues with that construct as std::void_t
and then
std::function<int(always_first_t<int, Args>...)> tmp = /* your lambda */;

recursion in variadic template function of different argument types

consider the following piece of code
template <int INDEX>
void foo() { } // termination version
template <int INDEX, typename Arg, typename... Args>
void foo(Arg head, Args... args) {
if (INDEX == 0) {
cout << head << endl;
}
else {
foo <INDEX-1 > (args...);
}
}
int main() {
foo<1> (1, 3.1415);
return 0;
}
the code compiles and outputs 3.1415 as expected.
however, the following simple code compiles fine but always outputs 1. do you have any fix for this?
template <int INDEX>
void foo() { } // termination version
template <int INDEX, typename Arg, typename... Args>
Arg foo(Arg head, Args... args) {
if (INDEX == 0) {
return head;
}
else {
foo <INDEX-1 > (args...);
}
}
int main() {
cout<<foo<1> (1, 3.1415,"Test!");
return 0;
}
in other words, how can I recursively call a variadic templated function with different argument types?
1 Problems with your approach
1.1 Missing return in foo<1>
Make sure you understand how a return from a nested call works. Your foo<1> calls foo<0> which returns its (foo<0>'s) first argument back to foo<1>. But your foo<1> does not care about foo<0>'s return because it called foo<0> like this:
else {
foo<i-1>(args...);// `i-1` becomes `0`
}
The compiler knows you have a problem here: Which value should foo<1> return after it got the return from foo<0> (which has been ignored)? It has to return a value of the same type as its first argument, but it never returns before reaching its closing }.
As pointed out in the comments, you should turn on compiler warnings to detect problems like these. In this case, -Wall (GCC documentation on warning options) is sufficient for GCC and clang to warn you (online demo), but there are more warnings available. If your filename reads main.cpp and the closing } is found line 23, column 1, the compiler warning could read
main.cpp: In function ‘Arg foo(Arg, Args ...) [with int INDEX = 1; Arg = int; Args = {double, const char*}]’:
main.cpp:23:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
1.2 Return type must be known at compile time
You might attempt to fix your code by passing the return value from foo<0> up the stack:
else {
return foo<i-1>(args...);// NOTE: type of return value depends on `foo<i-1>`
}
However, that fails because foo<1> has been declared to return a value of the same type as its first argument:
template<int i, class Arg, class... Args>
Arg foo(Arg, Args... args) {// <--------- NOTE: must return a value of type `Arg`
2 Fix for your own recursive implementation
2.1 C++17 and above
With C++17 you can use auto as return type together with constexpr if to implement the recursion as follows:
template<size_t i, class T0, class... Ts>
auto foo(T0 v0, Ts... vs) {
static_assert(i < 1u + sizeof...(Ts));
if constexpr(0u == i) return v0;// <------ NOTE: must be `if constexpr` (C++17)
else return foo<i-1u>(vs...);
}
2.2 C++14 and above
With C++14 you can also use auto as return type, but constexpr if is not available. The workaround is a well-known idiom and uses specialization of a class templates that "implements" the recursion logic:
template<int i>
struct foo_impl {
static_assert(i > 0, "the case `i == 0` requires a specialization");
template<class T0, class... Ts>
static auto get(T0, Ts... vs) {
return foo_impl<i-1>::get(vs...);
}
};
template<>
struct foo_impl<0> {
template<class T0, class... Ts>
static auto get(T0 v0, Ts...) {
return v0;
}
};
template<int i, class... Ts>
auto foo(Ts... vs) {
static_assert(i >= 0 && i < sizeof...(Ts), "index range: [0, size)");
return foo_impl<i>::get(vs...);// forward to "implementation"
}
2.3 C++11 and above
With C++11 you would need to specify trailing return types which is a bit tedious. See max66's answer for details.
3 Final recommendations
Enable and analyze compiler warnings (-Wall is an absolute minimum).
Once you are familiar with these techniques, do not implement this yourself. Instead, learn and use standard solutions like std::tuple.
Use compile-time recursion with caution. It may significantly increase your compilation time.
I don't think it's possible (in C++11 and C++14, at least) develop a foo() of this type because you don't know the correct return type.
If you don't want use std::tuple, I suggest to develop a type traits to extract the n-th type and manage foo() via SFINAE.
The following is a possible solution
#include <iostream>
#include <type_traits>
template <std::size_t, typename...>
struct indexType
{ using type = int; }; // the type of the foo() without argument
template <std::size_t I, typename I0, typename ... Is>
struct indexType<I, I0, Is...>
{ using type = typename indexType<I-1U, Is...>::type; };
template <typename I0, typename ... Is>
struct indexType<0U, I0, Is...>
{ using type = I0; };
template <std::size_t I, typename ... Args>
using indexType_t = typename indexType<I, Args...>::type;
template <std::size_t>
int foo ()
{ return 0; } // termination version: a return type is needed
template <std::size_t I, typename Arg, typename... Args>
auto foo (Arg const & head, Args const & ...)
-> typename std::enable_if<I == 0U, Arg>::type
{ return head; }
template <std::size_t I, typename Arg, typename... Args>
auto foo (Arg const &, Args const & ... args)
-> typename std::enable_if<I != 0U, indexType_t<I-1U, Args...>>::type
{ return foo<I-1U>(args...); }
int main ()
{
std::cout << foo<1U> (1, 3.1415, std::string("Test!")) << std::endl;
std::cout << foo<2U> (1, 3.1415, std::string("Test!")) << std::endl;
std::cout << foo<3U> (1, 3.1415, std::string("Test!")) << std::endl;
}

Variadic functions addition in c++ with constrained types

My goal is to do a simple addition of any number of parameters as long as they are of the following types -
integer (e.g. 123)
string represented as integer (e.g. "123")
If they are of some other type, I ignore them.
Approach
I'm using a variadic function approach.
Within the function, I check for type. If the type is int, then I add recursively. Else, I ignore the argument, and recur on further arguments.
Here's what I think the code looks like --
// BASE
template <typename T>
int func(T t)
{
string type= typeid(t).name();
if (type==typeid(int).name())
return stoi(t);
else if (type==typeid(const char*).name())
return atoi(t);
else
return 0;
}
// RECUR
template<typename T, typename... Args>
int func(T t, Args... args) // recursive variadic function
{
string type = typeid(t).name();
if (type==typeid(int).name()){
int sum = t;
return sum+func(args...);
}
else
return func(args...);
}
// MAIN
int main()
{
// All testing here in MAIN.
// [2]
int funcres = func('a',1, 2.5000,"123");
cout << funcres << endl;
return 0;
}
This gives me the expected answer: 124.
However,
I made following observations which tell me that my code is not fail-safe.
Why is stoi required in this line of the base function?
if (type==typeid(int).name())
return stoi(t);
If I do not do this and call just return t, I get an error when I call my function.
Cannot initialize return object of type 'int' with an lvalue of type 'const char *'
This doesn't make sense to me when I've already specified that return the integer if the type is integer.
Even after I do return stoi(t) (which I don't understand why is required in the first place), and return atoi(t) if the type is const char* then inserting "japan" or "123" at the beginning or in the middle in the template arg list [for.e.g func(1,2,2.5000,"123",12);] causes the code to complain at this point.
int sum = t;
The error is same as above.
Cannot initialize return object of type 'int' with an lvalue of type 'const char *'
Is using variadic function the best way or are there alternatives?
If it's the best way (it seems so to me since I need any number of parameters and any type of parameters to be considered for adding), what am I doing wrong?
C++ templates are resolved statically, meaning that substituting the parameters has to work for all substitutions, even those which are unreachable at runtime. However by using overloads with a helper function instead of RTTI we can handle the conversion a lot more cleanly:
template<class T>
int forceInt(T arg) { return 0; }
int forceInt(int arg) { return arg; }
int forceInt(std::string arg) { return std::stoi(arg); }
int forceInt(const char * arg) { return std::stoi(arg); }
With this helper function you can do a simple recursive sum:
int func() { return 0; }
template<typename T, typename... Args>
int func(T t, Args... args) // recursive variadic function
{
return forceInt(t) + func(args...);
}
This can also be expanded to handle any integer type. By using SFINAE on the general overload to restrict it to non-integer types, this causes the int overload to become prefered for integral types. However char is integral so we also need to add a char overload of 0 if you don't want that to be implicitly converted to an int:
template<class T, class U = typename std::enable_if<!std::is_integral<T>::value>::type>
int forceInt(T arg) { return 0; }
int forceInt(char arg) {return 0;}
Overloading is one possibility. You could also do it with some template magic. This has the advantage, that the list over which you are summing is pruned at compile time from all incompatible types (except the last element, which is substituted by 0 if it is no match).
#include <cassert>
#include <string>
#include <type_traits>
template < typename T >
struct is_const_char : std::false_type {};
template < >
struct is_const_char < const char * > : std::true_type {};
template < typename T >
struct is_int : std::false_type {};
template < >
struct is_int < int > : std::true_type {};
// Break condition
template < typename T >
typename std::enable_if<is_int<T>::value, int>::type
sum(T t)
{
return t;
}
template < typename T >
typename std::enable_if<is_const_char<T>::value, int>::type
sum(T t)
{
return std::stoi(t);
}
template < typename T >
typename std::enable_if<!is_int<T>::value && !is_const_char<T>::value, int>::type
sum(T)
{
return 0;
}
// Forward declarations
template < typename T, typename ... Args >
typename std::enable_if<is_const_char<T>::value, int>::type
sum(T, Args ...);
template < typename T, typename ... Args >
typename std::enable_if<is_int<T>::value, int>::type
sum(T, Args ...);
// Recursions
template < typename T, typename ... Args >
typename std::enable_if<!is_int<T>::value && !is_const_char<T>::value, int>::type
sum(T, Args ... args)
{
return sum(args...);
}
template < typename T, typename ... Args >
typename std::enable_if<is_int<T>::value, int>::type
sum(T t, Args ... args)
{
return t + sum(args...);
}
template < typename T, typename ... Args >
typename std::enable_if<is_const_char<T>::value, int>::type
sum(T t, Args ... args)
{
return std::stoi(t) + sum(args...);
}
// Test it
int main()
{
assert( sum('a', 1, 2, 3, "123", 4, 5) == 138 );
assert( sum('a',1, 2.5000,"123") == 124 );
}