Why don't optional parameters work with templates? - c++

Why is it that optional parameters with template functions don't work in C++?
(Clarification: I'm hoping to understand why C++ was designed such that this wouldn't be possible.)
#include <iostream>
template<class T1, class T2> T1 inc(T1 v, T2 u = 1) { return v + u; }
int main() { std::cout << inc(5); }
prog.cpp: In function ‘int main()’: error: no matching function for call to ‘inc(int)’

You got it the wrong way round. Default arguments don't participate in argument deduction:
Argument deduction happens first, as part of selecting the desired overload, and then the default arguments of that overload are filled in if necessary.

What Kerrek SB said is correct, the compiler just doesn't have enough to deduce T2 from (by what it's allowed to do from the standard).
In this particular case you can probably fix it by using only one template argument for everything, i.e.
template< class T > T inc( const T v, const T u = 1 ) { return v + u; }

Default arguments do not participate in the deduction process (only to overload resolution, and the rules are very difficult to remember -- always keep it simple).
To achieve what you want, you can provide an additional overload:
template <class T1, class T2> T1 inc(T1 v, T2 u) { return v + u; }
template <class T> T inc(T v) { return v + T(1); }

Template functions in C++ are generated at compile time, and are only generated if they are needed. So you could get a function generated like:
inc( int, int );
Which would be the T1 = int and T2 = int version. The compiler can implicitly determine the type if you pass in parameters for every template argument, so:
int a = 1;
int b = 2;
inc( a, b );
The compiler could generate a function like the one above at compile time, because it can infer that T1 = int and T2 = int. If however you do what you are doing:
inc( 5 );
The compiler can determine that T1 = int, but it cannot determine what T2 is. So no function is generated, and you get the error about the function not existing. This may be fixable if you use one template parameter:
template<class T> T inc(T v, T u = 1) { return v + u; }
Or if you provide an overload:
template<class T> T inc(T v) { return v + 1; }
There is also a third way:
inc<int, int>( 5 );
But I guess this isn't what you want...

Related

Can I change the template argument deduction order for a generic variadic lambda?

Take the following code, which is a simplified example:
template <typename F>
void foo(F f) {
//bool some = is_variadic_v<F>; // Scenario #1
bool some = true; // Scenario #2
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
A function takes a generic variadic lambda and calls it with a fixed set of arguments. This lambda itself then just calls another function/lambda with a matching prototype.
As one could expect, in scenario 2, when f is called inside foo, the compiler will deduce params... to be the parameter pack {1, 1}.
For scenario #1, I am using a code from another Q&A to deduce the arity of a callable object. If however such an object is callable with more than a pre-defined maximum amount of arguments, it is considered "variadic". In detail, is_variadic_v will employ a form of expression SFINAE where it is attempted to call the function object with a decreasing number of arguments having an "arbitrary type" that is implictly convertible to anything.
The problem is now that apparently, the compiler will deduce F (and along its argument pack) during this metacode, and if it is variadic (such as in this case), it deduces F as a lambda taking the dummy arguments, i.e. something like main()::lambda(<arbitrary_type<0>, arbitrary_type<1>, arbitrary_type<2>, ..., arbitrary_type<N>>) if N is the "variadic limit" from above. Now params... is deduced as arbitrary_type<1>, arbitrary_type<2>, ... and correspondingly, the call some(params...) will fail.
This behaviour can be demonstrated in this little code example:
#include <utility>
#include <type_traits>
#include <iostream>
constexpr int max_arity = 12; // if a function takes more arguments than that, it will be considered variadic
struct variadic_type { };
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hence, to 'simulate' an arbitrary function signature.
template <auto>
struct arbitrary_type {
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template <typename T>
operator T&&();
template <typename T>
operator T&();
};
template <
typename F, auto ...Ints,
typename = decltype(std::declval<F>()(arbitrary_type<Ints>{ }...))
>
constexpr auto test_signature(std::index_sequence<Ints...> s) {
return std::integral_constant<int, size(s)>{ };
}
template <auto I, typename F>
constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{ })) {
return { };
}
template <auto I, typename F, typename = std::enable_if_t<(I > 0)>>
constexpr auto arity_impl(...) {
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl<I - 1, F>(0);
}
template <typename F, auto MaxArity>
constexpr auto arity_impl() {
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl<MaxArity+1, F>(0);
if constexpr (tmp == MaxArity+1)
return variadic_type{ }; // if that works, F is considered variadic
else return tmp; // if not, tmp will be the correct arity of F
}
template <typename F, auto MaxArity = max_arity>
constexpr auto arity(F&&) { return arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, auto MaxArity = max_arity>
constexpr auto arity_v = arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, auto MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_type>;
template <typename F>
void foo(F f) {
bool some = is_variadic_v<F>;
//bool some = true;
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
Can I prevent this behaviour? Can I force the compiler to re-deduce the parameter list?
EDIT:
An additional peculiarity is that the compiler seems to act kind of schizophrenic. When I change the contents of foo to
foo([&some](auto... params) {
// int foo = std::index_sequence<sizeof...(params)>{ };
std::cout << sizeof...(params) << '\n';
});
the compiler will create a program that will print 2 in this example. If however I include the commented line (which, as it makes no sense, should trigger a compiler diagnostic), I get confronted with
error: cannot convert 'std::index_sequence<13>' {aka 'std::integer_sequence<long unsigned int, 13>'} to 'int' in initialization
85 | int foo = std::index_sequence<sizeof...(params)>{ };
so does the compiler now deduces sizeof...(params) to be 2 and 13 at the same time? Or did he change his mind and chooses now 13 just because I added another statement into the lambda? Compilation will also fail if I instead choose a static_assert(2 == sizeof...(params));. So the compiler deduces sizeof...(params) == 2, except if I ask him whether he did deduce 2, because then he didn't.
Apparently, it is very decisive for the parameter pack deduction what is written inside the lambda. Is it just me or does this behaviour really look pathologic?

No Automatic Cast from `int` to `float` with Template Function

After years of coding in c++, today I was asked a simple question, but indeed I couldn't find its answer and so here I am guys.
Besides wondering why this error is happening, I want to know how I can solve below error by modifying just the template function (without changing the main() function)
template <class T>
T Add(T first, T second)
{
return first + second;
}
int main()
{
auto sample_1 = Add(1, 2); // Works
auto sample_2 = Add(1.f, 2.f); // Works
auto sample_3 = Add(1.f, 2); // Error: no instance matches the argument types: (double, int)
return 0;
}
Besides wondering why this error is happening,
When you call Add(1.f, 2), the first argument type is float and the second argument type is int.
The compiler has to convert either the first argument to an int or the second argument to a float. Since both of them will require a conversion, they are equally good candidates. One cannot be preferred over the other.
I want to know how I can solve below error by modifying just the template function
You can change the function template to:
template <class T1, class T2>
auto Add(T1 first, T2 second)
{
return first + second;
}
or to (thanks #PiotrSkotnicki):
template <class T>
T Add(T first, decltype(first) second)
{
return first + second;
}
In this case, the type of second is not deduced from the argument being passed to the function. Type of first is deduced from the first argument and the type of second is forced to be the same as the type of first.
Add(1.2f, 2); // The first argument is deduced to be float
// The second argument is forced to be float.
Add(2, 1.2f); // The first argument is deduced to be int
// The second argument is forced to be int.
Just do:
template <class T1, class T2>
auto Add(T1 first, T2 second)
{
return first + second;
}
As with unique T, it is deduced once as int, once as double...
When you have
template <class T>
T Add(T first, T second)
the type of first and second need to be the same. If you want to take two different types then you can add a second template parameter
template <class T1, class T2>
auto Add(T1 first, T2 second)
or for C++11
template <class T1, class T2>
auto Add(T1 first, T2 second) -> decltype(first + second)
The compiler is trying to deduce the template type that it can use to create a function that matches the signature. Since the parameters are different types, it's unable to do so.
You could specify the type explicitly:
auto sample_3 = Add<float>(1.f, 2);
But you say you don't want to do that.
You can change the function to take two template types:
template <class T1, class T2>
T1 Add(T1 first, T2 second)
{
T1 p;
p = first + second;
return p;
}
But now you'll have to make an assumption about which type to return.
I've never tried to use auto as the return type, but apparently it works: http://ideone.com/1qO95w
template <class T1, class T2>
auto Add(T1 first, T2 second)
{
auto p = first + second;
return p;
}
Why write your own function when the standard already provided them?
In c++11, you can use:
#include <functional>
int main()
{
auto sample_1 = std::plus<float> () ( 1, 2 ); // Works
auto sample_2 = std::plus<float> () ( 1.f, 2.f ); // Works
auto sample_3 = std::plus<float> () ( 1.f, 2 ); // Works
return 0;
}
In c++14:
#include <functional>
int main()
{
auto sample_1 = std::plus<> () ( 1, 2 ); // Works
auto sample_2 = std::plus<> () ( 1.f, 2.f ); // Works
auto sample_3 = std::plus<> () ( 1.f, 2 ); // Works
return 0;
}
I want to know how I can solve below error by modifying just the template function
Like that:
template <class T1, class T2>
T1 Add(T1 first, T2 second)
{
T1 p;
p = first + second;
return p;
}
int main()
{
auto sample_1 = Add(1, 2);
auto sample_2 = Add(1.f, 2.f);
auto sample_3 = Add(1.f, 2);
return 0;
}

Template argument deduction in alias templates - typedefing any member function pointer

While answering a question, I proposed utilizing template aliases for typedefing the signature of a member function; that is, not just typedefing a member function but being able to factor out the target class that contains the method:
template<typename T>
using memberf_pointer = int (T::*)(int, int);
Though this seems to cover what the question asked, I tried to generalize it for arbitrary function arguments:
template<typename T, typename... Args>
using memberf_pointer = int (T::*)(Args&&...);
It fails with argument deduction issues (basically it assumes an empty arument list). Here's a demo:
#include <iostream>
class foo
{
public:
int g (int x, int y) { return x + y ; }
};
template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args&&...);
int main()
{
foo f ;
memberf_pointer<foo> mp = &foo::g ;
std::cout << (f.*mp) (5, 8) << std::endl ;
}
Why is this? Is there a way to get it to work?
The wording in both the title and body of the question is very misleading. There is zero template deduction going on in your example, anywhere. When you write:
memberf_pointer<foo> mp = &foo::g;
memberf_pointer<foo> is an alias template, yes, but it's a specific instantiation of one. There is no deduction going on because you are providing the exact type of mp. That line is exactly equivalent to:
int (foo:*mp)() = &foo::g;
which doesn't compile for the obvious reason that g takes arguments. The way to get template deduction in an assignment statement is to use auto:
auto mp = &foo::g;
The type of mp will be the same type as U had you called:
template <typename U> void meow(U );
meow(&foo::g);
which is to say, int (foo::*)(int, int).
Similarly, you could do:
decltype(&foo::g) mp = &foo::g;
Which would give you the same type as before.
Of course, even if you provided the correct argument list:
memberf_pointer<foo, int, int> mp = &foo::g;
That still wouldn't compile since your alias adds rvalue references to both arguments. The type of mp there is int (foo::*)(int&&, int&&), which would not match &foo::g. Perhaps you'd intended this to be deduction as if by forwarding-reference, but that is not the case here. In order to use the alias correctly you'd have to rewrite it:
template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args...);
memberf_pointer<foo, int, int> mp = &foo::g;
Had we had a member function that took an rvalue reference, we could then explicitly provide it:
class bar
{
public:
int h(int&& x, int&& y) { return x + y ; }
};
memberf_pointer<bar, int&&, int&&> mb = &bar::h;
Why not simply use an auto in this case? The only advantage of having a template in this case is being able to provide your types explicitly.
On the other hand, if you want your template to automatically deduce a function type, it needs to be directly parametrized on that type. If you also want it to provide all of the building blocks for you, the simplest way is to specialize it for functions or member functions. Example:
template<typename T> struct memberf_pointer_descriptor;
template<typename TOwner, typename TRet, typename... Args>
struct memberf_pointer_descriptor<TRet(TOwner::*)(Args...)>
{
// Your stuff goes here.
using type = TRet(TOwner::*)(Args...);
};
memberf_pointer_descriptor<decltype(&foo::g)>;
Or a function template that directly takes foo::g as an argument, to mitigate the need of using an explicit decltype. Depends on your needs.
A way to make your example work is the following:
#include <iostream>
class foo
{
public:
int g(int x, int y) { return x + y; }
};
template<typename T, typename...Args>
using memberf_pointer = int (T::*)(Args...);
int main()
{
foo f;
memberf_pointer<foo, int, int> mp = &foo::g;
std::cout << (f.*mp) (5, 8) << std::endl;
}
It removes the reference on the variadic template parameter and when instantiating the memberf_pointer it supplies also the member function parameters. But auto is probably the way to go...
C++ doesn't feature something like a Hindley-Milner type deduction system and it won't work for your specific rvalue assignment
memberf_pointer<foo> mp = &foo::g ;
As for some quick workarounds you could
Just drop the whole struct and use auto
auto mp = &foo::g;
Explicitly provide the types or the pointer type
template<typename T>
using memberf_pointer = T;
memberf_pointer<decltype(&foo::g)> mp = &foo::g;
Cfr. Template argument deduction

How to make a SFINAE-based Y combinator in C++?

I was thinking about the implicit templates of C++14, and I'm trying to declare a function to match an specific argument type (SFINAE and traits still give me headaches). I'm not sure how to explain what I want, but I'm trying to make a Y combinator (just to see if it's possible, not intended for production).
I'm trying to declare a function:
template<typename T>
my_traits<T>::return_type Y(T t) {
// ...
};
Such that T is a function (or a functor) that matches
std::function<R(F, Args...)>
// where F (and above return_type) will be
std::function<R(Args...)>
Which would take any number of arguments, but the first should be a function with the same return type and the same arguments (except this function itself). The first parameter to the operator () of the functor is a template.
The usage I want to achieve:
auto fib = [](auto myself, int x) {
if(x < 2)
return 1;
return myself(x - 1) + myself(x - 2);
};
// The returned type of fib should be assignable to std::function<int(int)>
I wasn't able to take the return type of the T type (because of the overloaded operator ()). What I'm trying to make is possible? How could I make it?
Edit:
Seeing it from a different angle, I'm trying to make this work:
struct my_functor {
template<typename T>
char operator () (T t, int x, float y) { /* ... */ };
};
template<typename T>
struct my_traits {
typedef /* ... */ result_type;
/* ... */
};
// I want this to be std::function<char(int, float)>, based on my_functor
using my_result =
my_traits<my_functor>::result_type;
It is not possible in C++14 return type deduction to deduce int(int) out of int(T, int) as OP desires.
However, we can mask the first parameter of the result using the following approach. The struct YCombinator is instantiated with a non-recursive function object member, whose first argument is a version of itself without the first argument. YCombinator provides a call operator that receives the arguments of the non-recursive function and then returns its function object member after substituting itself for the first argument. This technique allows the programmer to avoid the messiness of myself(myself, ...) calls within the definition of the recursive function.
template<typename Functor>
struct YCombinator
{
Functor functor;
template<typename... Args>
decltype(auto) operator()(Args&&... args)
{
return functor(*this, std::forward<Args>(args)...);
}
};
A make_YCombinator utility template allows for a streamlined usage pattern. This compiles run runs in GCC 4.9.0.
template<typename Functor>
decltype(auto) make_YCombinator(Functor f) { return YCombinator<Functor> { f }; }
int main()
{
auto fib = make_YCombinator([](auto self, int n) -> int { return n < 2 ? 1 : self(n - 1) + self(n - 2); });
for (int i = 0; i < 10 ; ++i)
cout << "fib(" << i << ") = " << fib(i) << endl;
return 0;
}
Since the non-recursive function is not defined at time that the recursive function is defined, in general the recursive function must have an explicit return type.
Edit:
However, it may be possible for the compiler to deduce the return type in certain cases if the programmer takes care to indicate the return type of the recursive function before use of the non-recursive function. While the above construction requires an explicit return type, in the following GCC 4.9.0 has no problem deducing the return type:
auto fib = make_YCombinator([](auto self, int n) { if (n < 2) return 1; return self(n - 1) + self(n - 2); });
To pin this down just a bit further, here is a quote from the draft C++14 standard on return type deduction [7.1.6.4.11]:
If the type of an entity with an undeduced placeholder type is needed
to determine the type of an expression, the program is ill-formed.
Once a return statement has been seen in a function, however, the
return type deduced from that statement can be used in the rest of the
function, including in other return statements. [ Example:
auto n = n; // error, n’s type is unknown
auto f();
void g() { &f; } // error, f’s return type is unknown
auto sum(int i) {
if (i == 1)
return i; // sum’s return type is int
else
return sum(i-1)+i; // OK, sum’s return type has been deduced
}
—end example ]
It's a really hacky approach, and has severe limitations, but here it goes:
First, we need a class that pretends to support every possible operation (as far as possible), such as the fake_anything class. Note that this isn't perfect since at a minimum . and :: won't work. To fake a functor, we give it a function call operator:
template<class... Ts> fake_anything operator()(Ts&&...) const;
Knowing that the lambda has only one operator(), and that operator() has only one template parameter allows us to extract its signature with decltype(&T::operator()<fake_anything>).
For this to work, the lambda's return type must be explicitly specified; it can't use deduction, since otherwise the deduced return types will probably conflict.
Finally we can obtain the other arguments to the lambda and the return type using the standard partial specialization approach:
template<class T>
struct extract_signature;
template<class T, class R, class FA, class...Args>
struct extract_signature<R (T::*)(FA, Args...)> {
static_assert(std::is_same<fake_anything, std::decay_t<FA>>::value, "Unexpected signature");
using type = std::function<R(Args...)>;
};
template<class T, class R, class FA, class...Args>
struct extract_signature<R (T::*)(FA, Args...) const> {
static_assert(std::is_same<fake_anything, std::decay_t<FA>>::value, "Unexpected signature");
using type = std::function<R(Args...)>;
};
// other cv- and ref-qualifier versions omitted - not relevant to lambdas
// we can also static_assert that none of Args is fake_anything, or reference to it, etc.
And add an alias template to hide all the ugliness of the hack:
template<class T>
using signature_t = typename extract_signature<decltype(&T::template operator()<fake_anything>)>::type;
And finally we can check that
static_assert(std::is_same<signature_t<decltype(fib)>,
std::function<int(int)>>::value, "Oops");
Demo.
The limitations:
The return type of operator() must be explicitly specified. You cannot use automatic return type deduction, unless all of the return statements return the same type regardless of the return type of the functor.
The faking is very imperfect.
This works for operator() of a particular form only: template<class T> R operator()(T, argument-types...) with or without const, where the first parameter is T or a reference to possibly cv-qualified T.

Higher-order functions in C++11

I'm trying to write a generic fold function using the new anonymous functions available in C++11, here is what I have:
template<typename T>
T foldl(std::function<T(T,T)> f, T initial, std::vector<T> items) {
T accum = initial;
for(typename std::vector<T>::iterator it = items.begin(); it != items.end(); ++it) {
accum = f(accum, (*it));
}
return accum;
}
The following attempt to use it:
std::vector<int> arr;
arr.assign(8, 2);
foldl([] (int x, int y) -> int { return x * y; }, 1, arr);
causes an error:
main.cpp:44:61: error: no matching function for call to 'foldl(main(int, char**)::<lambda(int, int)>, int, std::vector<int>&)'
main.cpp:44:61: note: candidate is:
main.cpp:20:3: note: template<class T> T foldl(std::function<T(T, T)>, T, std::vector<T>)
main.cpp:20:3: note: template argument deduction/substitution failed:
main.cpp:44:61: note: 'main(int, char**)::<lambda(int, int)>' is not derived from 'std::function<T(T, T)>'
It seems to me that using std::function is not the right way to go about defining the type of f. How can I correct this?
Your code is not very generic. There is no need to require a function, vector, or anything of the kind. And generally, in C++, functions would go at the end of the argument list (especially important for lambdas, as they can be big).
So it would be better (ie: more standard) to write this as this:
template<typename Range, typename Accum>
typename Range::value_type foldl(const Range &items, const typename Range::value_type &initial, Accum f)
{
typename Range::value_type accum = initial;
for(const auto &val : items) {
accum = f(accum, val);
}
return accum;
}
Or you could just use std::accumulate which does the exact same thing.
I'm not certain why this template is failing, but switching over to using a template parameter for the function instead of std::function<> seems to work wonderfully.
template<typename T, typename F>
T foldl(F f, T initial, std::vector<T> items) {
If you simply convert it to an std::function first, and then use that, it will work:
std::vector<int> arr;
arr.assign(8, 2);
std::function<int(int,int)> f = [] (int x, int y) -> int { return x * y; };
foldl(f, 1, arr);
The problem is that lambda is a different type than std::function. Each lambda is a separate type. Although a lambda (and all other function objects) is convertible to a std::function with appropriate type parameter, the compiler has no idea what template argument for T would make it convertible. (We happen to know that T = int would work, but there is no general way to figure it out.) So it cannot compile it.