I want to default a lambda argument in a template function but it fails to compile. What am I missing?
template <typename F>
void foo(
F f = [](){
std::cout << "Hello!\n";
}
) {
f();
}
int main() {
foo(); // 1. does not compile
foo([](){ // 2. this is ok
std::cout << "Hello!\n";
});
}
You can't deduce the template parameter of a function from the default function arguments. See this question for details on why this restriction is in place.
So you must provide a default template parameter yourself. Since you need both the type and the value of the lambda, a simple way to do this would be to write the lambda once, and then use it inside the function template.
auto lambda = []()
{
std::cout << "Bye!\n";
};
template <typename F = decltype(lambda)> // default parameter
void foo(F f = lambda) // default value
{
f();
}
Here's a demo
Related
So I'm trying to do something like the following
int x = 123;
myFunction(x, [](auto y) {
std::cout << y;
});
And myFunction is defined as
template <typename T>
void myFunction(T val, void(*callback)(T)) {
callback(val);
}
When trying to compile the code clang gives me the error could not match 'void (*)(T)' against '(lambda at ...)'. I've figured out this is because you can't deduce the type from a lambda.
So that's actually alright because what I actually want is the parameter type of the callback to be the type deduced from the val parameter of myFunction.
So my question put simply is, is it possible somehow to exclude the callback parameter from being deduced and instead just use the deduced type of val?
is it possible somehow to exclude the callback parameter from being deduced and instead just use the deduced type of val?
Sure.
If you define something like this
template <typename T>
struct self
{ using type = T; };
you can define myFunction() as follows
template <typename T>
void myFunction(T val, void(*callback)(typename self<T>::type)) {
callback(val);
}
Now T is deduced from val and used for callback.
Starting from C++20, you can avoid the self definition and use the newly introduced std::type_identity_t
template <typename T>
void myFunction(T val, void(*callback)(std::type_identity_t<T>)) {
callback(val);
}
One option (and there are a few) is to pass the callable item as a template argument and allow templating to take care of some of the details for you - eg:
template <typename T, typename FUNCTOR>
void myFunction(T val, FUNCTOR callback) {
callback(val);
}
// Note: could be a const ref:
//void myFunction(T val, const FUNCTOR &callback) {
int main()
{
int some_int{1};
myFunction(some_int, [](auto y){ std::cout << y << std::endl; });
float some_float{1.1f};
myFunction(some_float, [](auto y){ std::cout << y << std::endl; });
return 0;
}
Full example here (following your code - but this could be further tidied): https://godbolt.org/z/qr7T5GPd6
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?
Considering the following toy example:
#include <iostream>
template <typename T>
void foo(T func, int *i) {
if (i) {
func(*i);
} else {
func();
}
}
int main() {
auto n = new int{2};
foo([](int x) { std::cout << x << std::endl; }, n);
foo([]() { std::cout << "Foo" << std::endl; }, nullptr);
return 0;
}
I want to create a function which receives lambdas functions with different signatures and call them inside its scope. If I do not call the lambda function inside foo, it compiles fine, but if I call it, the code does not compile.
Is there any way of receiving lambdas with different signatures and call it in a function?
Inside your function template, T has to be determined as one specific type, and remains that type throughout the entirety of that instantiation.
You can, however, accomplish what you want by making foo a variadic template, and passing the parameter pack through to the lambda that was passed in:
#include <iostream>
template <typename T, typename ...Args>
void foo(T func, Args... args) {
func(std::forward<Args>(args)...);
}
int main() {
auto n = new int{ 2 };
foo([](int x) { std::cout << x << std::endl; }, n[0]);
foo([]() { std::cout << "Foo" << std::endl; });
foo([](int a, int b) { std::cout << a + b << "\n"; }, 3, 2);
}
This way foo only needs to contain one invocation of func, which has a consistent type throughout each instantiation--but can vary from one instantiation to the next.
In this case, when the function you're passing doesn't take any arguments, you don't pass a special value for the argument. If it doesn't take an argument, you just don't pass an argument.
In the first case where you instantiate the template function with:
foo([](int x) { std::cout << x << std::endl; }, n);
The compiler creates the function where (T func) is a callable taking an int as function, therefore you can't do:
void foo(T func, int *i) {
if (i) {
func(*i);
}
//else
//{
// func(); // Can't call T func with no argument.
//} // Commenting this out will compile.
}
When you instantiate the template function with:
foo([]() { std::cout << "Foo" << std::endl; }, nullptr);
The compiler creates the function:
void foo(T func, int *i); // Where T func is a callable taking no arguments
So, in contrast to the previous example, you must must call T func with no arguments given. Therefore you can't do:
template <typename T>
void foo(T func, int *i)
{
//if (i) {
// func(*i); // Can't call T func with one argument, because
// T func function template has been instantiated
// as T func taking no arguments.
//}
//else
{
func(); // This obviously is fine.
}
}
Consider this code (which compiles on GCC and MSVC):
int main()
{
auto foo = [](auto p){
typedef decltype(p) p_t;
auto bar = [](){
return static_cast<p_t>(10);
};
return bar();
};
std::cout << foo(0ull) << std::endl;
}
foo() is a templated lambda because it has an auto parameter. But for bar() to know the type p_t, it must somehow be implicitly templated too, which then leads me to the question in the title:
Are all lambdas inside templated lambdas also templated lambdas?
If that's the case, then it seems like the number of templates parameters will grow pretty quickly if I have lots of nested lambdas (not necessarily a bad thing, but it comes as a surprise to me).
I'm not sure if you can actually say that a lambda is templated. The type of lambda with auto template parameter is not a template at all in the sense it does not match to template template parameter:
#include <iostream>
auto foo = [](auto param){};
template <class T>
struct functor_template {
void operator()() const { }
};
template <template <class...> class Foo, class... Ts>
void bar(Foo<Ts...>) {
}
int main() {
//bar(foo); //prog.cc:7:6: note: template argument deduction/substitution failed
bar(functor_template<int>{});
}
The reason for that is quite simple - only thing that is very close to be called template in such lambdas is their operator().
But I think you wanted to ask more if the type of lambda inside the lambda with auto parameter(s) is depended on parameters' types passed to that lambda. The answer is - yes. This can be easily tested:
#include <iostream>
#include <type_traits>
auto foo = [](auto p){
static_cast<void>(p);
typedef decltype(p) p_t;
auto bar = [](){
return static_cast<p_t>(10);
};
return bar;
};
int main() {
static_cast<void>(foo);
std::cout << std::is_same<decltype(foo(int{})), decltype(foo(float{}))>::value << std::endl;
std::cout << std::is_same<decltype(foo(int{})), decltype(foo(int{}))>::value << std::endl;
}
Output:
0
1
I'm trying to implement a function template (in C++11) whose parameter is a lambda with arbitrary parameters, and return a compatible std::function object. The goal is for the returned function when called to invoke the original lambda asynchronously, but for now I'm just returning the original lambda.
The problem is simply getting the compiler to accept a lambda as the parameter of the function template. Here are some simple templates:
#include <functional>
using namespace std;
template <class Arg>
function<void(Arg)> pass1(function<void(Arg)> fn) {
return fn;
}
template <class... Args>
function<void(Args...)> passn(function<void(Args...)> fn) {
return fn;
}
They do the same thing, it's just that pass1 only works on single-parameter functors while passn takes an arbitrary number.
So now we try using them, first pass1:
auto p1 = pass1( [](int a)->void {cout << a;} ); // ERROR
This doesn't work; the compiler can't seem to tell what parameters the lambda takes. Clang error message is:
Untitled.cpp:17:12: error: no matching function for call to 'pass1'
auto p1 = pass1( [](int a)->void {cout << a;} );
^~~~~
Untitled.cpp:6:21: note: candidate template ignored: could not match 'function<void (type-parameter-0-0)>' against '(lambda at Untitled.cpp:17:19)'
function<void(Arg)> pass1(function<void(Arg)> fn) {
I can work around this by explicitly specifying the template parameter type:
auto p2 = pass1<int>( [](int a)->void {cout << a;} ); // OK
However, this workaround fails with passn:
auto p3 = passn<int>( [](int a)->void {cout << a;} );
Untitled.cpp:23:12: error: no matching function for call to 'passn'
auto p3 = passn<int>( [](int a)->void {cout << a;} );
^~~~~~~~~~
Untitled.cpp:11:25: note: candidate template ignored: could not match 'function<void (int, type-parameter-0-0...)>' against '(lambda at Untitled.cpp:23:24)'
function<void(Args...)> passn(function<void(Args...)> fn) {
^
The weird thing is that I can invoke passn if I pass it a function object:
function<void(int)> fn = [](int a)->void {cout << a;};
auto x = passn<int>(fn); // OK
...in fact, I don't even have to specify the template parameter type:
auto y = passn(fn); // OK
The function I actually need is going to be like passn, but I don't want the extra verbiage of having to wrap a function object around the lambda every time I call it. Am I missing something, or is this just not possible? Would it be possible in C++14?
You can use this implementation of passn:
#include <functional>
#include <iostream>
template <class RetVal, class T, class... Args>
std::function<RetVal(Args...)> get_fun_type(RetVal (T::*)(Args...) const);
template <class RetVal, class T, class... Args>
std::function<RetVal(Args...)> get_fun_type(RetVal (T::*)(Args...));
template <class T>
auto passn(T t) -> decltype(get_fun_type(&T::operator())) {
return t;
}
int main() {
auto fun = passn([](int a) { std::cout << a; });
fun(42);
}
(demo)
It assumes you pass in a type that has an operator(). It takes the address of that function and deduces parameters from that member pointer.
The function will fail if you pass it an object that has multiple operator()s because then taking its address will be ambiguous, but lambdas will not produce that problem.