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 */;
Related
Let's create currying function.
template <typename TFunc, typename TArg>
class CurryT
{
public:
CurryT(const TFunc &func, const TArg &arg)
: func(func), arg(arg )
{}
template <typename... TArgs>
decltype(auto) operator()(TArgs ...args) const
{ return func(arg, args...); }
private:
TFunc func;
TArg arg ;
};
template <typename TFunc, typename TArg>
CurryT<decay_t<TFunc>, remove_cv_t<TArg>>
Curry(const TFunc &func, const TArg &arg)
{ return {func, arg}; }
And function that decouple function to single argument functions:
// If single argument function (F(int)).
template <typename F>
static auto Decouple(const F &f, enable_if_t<is_invocable_v<F, int>> * = nullptr)
{
return f;
}
// If multiple arguments function (F(int, int, ...)).
template <typename F>
static auto Decouple(const F &f, enable_if_t<!is_invocable_v<F, int>> * = nullptr)
{
return [f](int v) { return Decouple( Curry(f, v) ); };
}
Everything works fine if 2 arguments function is passed:
auto f1 = Decouple(
[](int a, int b)
{ std::cout << a << " " << b << std::endl; }
);
f1(3)(4); // Outputs 3 4
But if I add more arguments
auto f2 = Decouple(
[](int a, int b, int c)
{ std::cout << a << " " << b << " " << c << std::endl; }
);
f(5)(6)(7);
The compilation breaks: https://coliru.stacked-crooked.com/a/10c6dba670d17ffa
main.cpp: In instantiation of 'decltype(auto) CurryT<TFunc, TArg>::operator()(TArgs ...) const [with TArgs = {int}; TFunc = main()::<lambda(int, int, int)>; TArg = int]':
main.cpp:17:26: error: no match for call to '(const main()::<lambda(int, int, int)>) (const int&, int&)'
17 | { return func(arg, args...); }
It breaks in instantiation of std::is_invocable.
Since debugging the standard library is hard, I created simple versions of standard type traits classes:
template <typename F> true_type check(const F &, decltype( declval<F>()(1) )* );
template <typename F> false_type check(const F &, ...);
template <typename F>
struct invocable_with_int : decltype(check(declval<F>(), nullptr))
{};
template <typename F>
inline constexpr bool invocable_with_int_v = invocable_with_int<F>::value;
template<bool B>
struct my_enable_if {};
template<>
struct my_enable_if<true>
{ using type = void; };
template <bool B>
using my_enable_if_t = typename my_enable_if<B>::type;
The problem remains the same https://coliru.stacked-crooked.com/a/722a2041600799b0:
main.cpp:29:73: required by substitution of 'template<class F> std::true_type check(const F&, decltype (declval<F>()(1))*) [with F = CurryT<main()::<lambda(int, int, int)>, int>]'
It tries to resolve calling to this function:
template <typename F> true_type check(const F &, decltype( declval<F>()(1) )* );
But decltype (declval<F>()(1))*) fails. But shouldn't this function be removed from overload resolution because template substitution fails? It works when Decouple is called first time. But when it is called second time the SFINAE seems to be disabled, and the first failure of template substitution gives a compilation error. Are there some limitation on secondary SFINAE? Why calling template function recursively doesn't work?
The problem is reproduced in GCC and Clang. So it is not a compiler bug.
Your operator() overload is completely unconstrained and therefore claims to be callable with any set of arguments. Only declarations, not definitions, are inspected to determine which function to call in overload resolution. If substitution into the definition then fails, SFINAE does not apply.
So, constrain your operator() to require TFunc to be callable with TArg and TArgs... as arguments.
For example:
template <typename... TArgs>
auto operator()(TArgs ...args) const -> decltype(func(arg, args...))
For me it is strange that your CurryT::operator() accepts unknown number of arguments.
Since aim is to have a functions which accept only one argument I expected that this function will accept only one argument.
IMO depending what kind of function CurryT holds CurryT::operator() should return a different type: return type of starting function or another version of CurryT.
Here is my approach using std::bind_front from C++20:
namespace detail {
template <typename TFunc>
class CurryT
{
public:
explicit CurryT(TFunc f) : mF(std::move(f))
{}
template<typename T>
auto get(T&& x, int = 0) -> decltype(std::declval<TFunc>()(x)) {
return mF(x);
}
template<typename T>
auto get(T&& x, char) {
return CurryT<decltype(std::bind_front(mF, std::forward<T>(x)))>{
std::bind_front(mF, std::forward<T>(x))
};
}
template<typename T>
auto operator()(T&& x)
{
return this->get(std::forward<T>(x), 1);
}
private:
TFunc mF;
};
}
template<typename F>
auto Decouple(F&& f)
{
return detail::CurryT<std::decay_t<F>>{std::forward<F>(f)};
}
https://godbolt.org/z/eW9r4Y6Ea
Note with this approach integer argument is not forced like in your solution.
I have the following code which implements a memoize function.
note The question is not about how to write a memoize function specifically but rather about the compile error I get with this implementation and the smallest change to get it to work.
The implementation.
#include <functional>
#include <map>
#include <functional>
#include <iostream>
using namespace std;
template<typename T>
struct memfun_type
{
using type = void;
};
template<typename Ret, typename Class, typename... Args>
struct memfun_type<Ret(Class::*)(Args...) const>
{
using type = std::function<Ret(Args...)>;
};
template<typename F>
typename memfun_type<decltype(&F::operator())>::type
FFL(F const &func)
{ // Function from lambda !
return func;
}
template <typename ReturnType, typename... Args>
std::function<ReturnType (Args...)>
memoizeImp(std::function<ReturnType (Args...)> func)
{
std::map<std::tuple<Args...>, ReturnType> cache;
return ([=](Args... args) mutable {
std::tuple<Args...> t(args...);
if (cache.find(t) == cache.end())
cache[t] = func(args...);
return cache[t];
});
}
template <typename Fn>
auto memoize(Fn && fn){
return memoizeImp(FFL(fn));
}
and test program
int main()
{
auto a = 2.;
auto foo = [a](double x){return x+a;};
auto foom = memoize(foo);
std::cout << foo(1) << std::endl;
std::cout << foom(1) << std::endl;
}
the output as expected is
3
3
However if I make a small change to the test program changing
auto foo = [a](double x){return x+a;};
to
auto foo = [a](double x)mutable{return x+a;};
I get the following compile error on gcc
Could not execute the program
Compiler returned: 1
Compiler stderr
<source>: In instantiation of 'auto memoize(Fn&&) [with Fn = main()::<lambda(double)>&]':
<source>:49:24: required from here
<source>:42:26: error: invalid use of void expression
42 | return memoizeImp(FFL(fn));
| ~~~^~~~
<source>: In instantiation of 'typename memfun_type<decltype (& F::operator())>::type FFL(const F&) [with F = main()::<lambda(double)>; typename memfun_type<decltype (& F::operator())>::type = void; decltype (& F::operator()) = double (main()::<lambda(double)>::*)(double)]':
<source>:42:26: required from 'auto memoize(Fn&&) [with Fn = main()::<lambda(double)>&]'
<source>:49:24: required from here
<source>:24:12: error: return-statement with a value, in function returning 'memfun_type<double (main()::<lambda(double)>::*)(double)>::type' {aka 'void'} [-fpermissive]
24 | return func;
| ^~~~
The failing code and compile error can be viewed and tested at https://godbolt.org/z/74PKWvqr4
I'm not sure what the fix is to make it work with mutable lambdas.
You are lacking a specialisation.
Adding this makes it work
template<typename Ret, typename Class, typename... Args>
struct memfun_type<Ret(Class::*)(Args...)>
{
using type = std::function<Ret(Args...)>;
};
The operator() of the closure-type of a lambda is const qualified iff the lambda is not declared mutable
In the below program, when mutable is not used, the program fails to compile.
#include <iostream>
#include <queue>
#include <functional>
std::queue<std::function<void()>> q;
template<typename T, typename... Args>
void enqueue(T&& func, Args&&... args)
{
//q.emplace([=]() { // this fails
q.emplace([=]() mutable { //this works
func(std::forward<Args>(args)...);
});
}
int main()
{
auto f1 = [](int a, int b) { std::cout << a << b << "\n"; };
auto f2 = [](double a, double b) { std::cout << a << b << "\n";};
enqueue(f1, 10, 20);
enqueue(f2, 3.14, 2.14);
return 0;
}
This is the compiler error
lmbfwd.cpp: In instantiation of ‘enqueue(T&&, Args&& ...)::<lambda()> [with T = main()::<lambda(int, int)>&; Args = {int, int}]’:
lmbfwd.cpp:11:27: required from ‘struct enqueue(T&&, Args&& ...) [with T = main()::<lambda(int, int)>&; Args = {int, int}]::<lambda()>’
lmbfwd.cpp:10:2: required from ‘void enqueue(T&&, Args&& ...) [with T = main()::<lambda(int, int)>&; Args = {int, int}]’
lmbfwd.cpp:18:20: required from here
lmbfwd.cpp:11:26: error: no matching function for call to ‘forward<int>(const int&)’
func(std::forward<Args>(args)...);
I am not able to understand why argument forwarding fails without mutable.
Besides, if I pass a lambda with string as argument, mutable is not required and program works.
#include <iostream>
#include <queue>
#include <functional>
std::queue<std::function<void()>> q;
template<typename T, typename... Args>
void enqueue(T&& func, Args&&... args)
{
//works without mutable
q.emplace([=]() {
func(std::forward<Args>(args)...);
});
}
void dequeue()
{
while (!q.empty()) {
auto f = std::move(q.front());
q.pop();
f();
}
}
int main()
{
auto f3 = [](std::string s) { std::cout << s << "\n"; };
enqueue(f3, "Hello");
dequeue();
return 0;
}
Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?
A non-mutable lambda generates a closure type with an implicit const qualifier on its operator() overload.
std::forward is a conditional move: it is equivalent to std::move when the provided template argument is not an lvalue reference. It is defined as follows:
template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;
template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;
(See: https://en.cppreference.com/w/cpp/utility/forward).
Let's simplify your snippet to:
#include <utility>
template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)
{
[=] { func(std::forward<Args>(args)...); };
}
int main()
{
enqueue([](int) {}, 10);
}
The error produced by clang++ 8.x is:
error: no matching function for call to 'forward'
[=] { func(std::forward<Args>(args)...); };
^~~~~~~~~~~~~~~~~~
note: in instantiation of function template specialization 'enqueue<(lambda at wtf.cpp:11:13), int>' requested here
enqueue([](int) {}, 10);
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
^
note: candidate function template not viable: 1st argument ('const int')
would lose const qualifier
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
^
In the snippet above:
Args is int and refers to the type outside of the lambda.
args refers to the member of the closure synthesized via lambda capture, and is const due to the lack of mutable.
Therefore the std::forward invocation is...
std::forward<int>(/* `const int&` member of closure */)
...which doesn't match any existing overload of std::forward. There is a mismatch between the template argument provided to forward and its function argument type.
Adding mutable to the lambda makes args non-const, and a suitable forward overload is found (the first one, which moves its argument).
By using C++20 pack-expansion captures to "rewrite" the name of args, we can avoid the mismatch mentioned above, making the code compile even without mutable:
template <typename T, typename... Args>
void enqueue(T&& func, Args&&... args)
{
[func, ...xs = args] { func(std::forward<decltype(xs)>(xs)...); };
}
live example on godbolt.org
Why is mutable required in case of int double and not in case of string ? What is the difference between these two ?
This is a fun one - it works because you're not actually passing a std::string in your invocation:
enqueue(f3, "Hello");
// ^~~~~~~
// const char*
If you correctly match the type of the argument passed to enqueue to the one accepted by f3, it will stop working as expected (unless you use mutable or C++20 features):
enqueue(f3, std::string{"Hello"});
// Compile-time error.
To explain why the version with const char* works, let's again look at a simplified example:
template <typename T>
void enqueue(T&& func, const char (&arg)[6])
{
[=] { func(std::forward<const char*>(arg)); };
}
int main()
{
enqueue([](std::string) {}, "Hello");
}
Args is deduced as const char(&)[6]. There is a matching forward overload:
template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;
After substitution:
template< class T >
constexpr const char*&& forward( const char*&& t ) noexcept;
This simply returns t, which is then used to construct the std::string.
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.
Having the following piece of code:
#include <iostream>
#include <type_traits>
template <typename F,
typename = typename std::enable_if<
std::is_function< F >::value
>::type>
int fun( F f ) // line 8
{
return f(3);
}
int l7(int x)
{
return x%7;
}
int main()
{
auto l = [](int x) -> int{
return x%7;
};
fun(l); // line 23
//fun(l7); this will also fail even though l7 is a regular function
std::cout << std::is_function<decltype(l7)>::value ; // prints 1
}
I will get the following error:
main2.cpp: In function ‘int main()’:
main2.cpp:23:8: error: no matching function for call to ‘fun(main()::<lambda(int)>&)’
fun(l);
^
main2.cpp:8:5: note: candidate: template<class F, class> int fun(F)
int fun( F f )
^
main2.cpp:8:5: note: template argument deduction/substitution failed:
main2.cpp:5:11: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
typename = typename std::enable_if<
^
When I comment out the std::enable_if template parameter then it compiles and runs just fine. Why?
From cppreference:
Checks whether T is a function type. Types like std::function, lambdas, classes with overloaded operator() and pointers to functions don't count as function types.
This answer explains that you also need to use std::remove_pointer<F>::type as the type since functions are converted to pointers to functions when passing by value. So your code should look like this:
template <typename F,
typename = typename std::enable_if<
std::is_function<
typename std::remove_pointer<F>::type
>::value
>::type>
int fun( F f )
{
return f(3);
}
Another way to approach this problem is to write a more specific type trait. This one, for example, checks that the argument types are convertible and works for anything that's callable.
#include <iostream>
#include <type_traits>
#include <utility>
#include <string>
template<class T, class...Args>
struct is_callable
{
template<class U> static auto test(U*p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type());
template<class U> static auto test(...) -> decltype(std::false_type());
static constexpr auto value = decltype(test<T>(nullptr))::value;
};
template<class T, class...Args>
static constexpr auto CallableWith = is_callable<T, Args...>::value;
template <typename F,
std::enable_if_t<
CallableWith<F, int>
>* = nullptr
>
int fun( F f ) // line 8
{
return f(3);
}
int l7(int x)
{
return x%7;
}
int main()
{
auto l = [](int x) -> int{
return x%7;
};
std::cout << "fun(l) returns " << fun(l) << std::endl;
std::cout << CallableWith<decltype(l7), int> << std::endl; // prints 1
std::cout << CallableWith<decltype(l7), float> << std::endl; // prints 1 because float converts to int
std::cout << CallableWith<decltype(l7), const std::string&> << std::endl; // prints 0
}
Have a look at std::is_invocable which also covers lambdas in C++17 (std::is_callable does not exist).