#include <memory>
#include <iostream>
class Manager
{
public:
Manager() {}
virtual ~Manager() {}
int funcA(std::shared_ptr<int> a, float b) { return *a + b; }
int funcA(std::shared_ptr<double> a) { return *a; }
};
template <typename T, typename... Args>
auto resolver(int (Manager::*func)(std::shared_ptr<T>, Args...)) -> decltype(func) {
return func;
}
int main(int, char **)
{
Manager m;
Manager *ptr = &m;
auto var = std::make_shared<int>(1);
int result = (ptr->*resolver<int>(&Manager::funcA))(var, 2.0);
std::cout << result << std::endl;
return 0;
}
This code fail to compile with gcc but is fine with clang.
(gcc 5.3.1 and 6.0.0 20151220).
Do you know if there is any solution to make it compile with gcc ? I tried with template specialization and explicit instantiation.
EDIT: gcc gives the following error:
test > g++ -std=c++11 test.cpp
test.cpp: In function 'int main(int, char**)':
test.cpp:29:52: error: no matching function for call to 'resolver(<unresolved overloaded function type>)'
int result = (ptr->*resolver<int>(&Manager::funcA))(var, 2.0);
^
test.cpp:15:6: note: candidate: template<class T, class ... Args> decltype (func) resolver(int (Manager::*)(std::shared_ptr<_Tp1>, Args ...))
auto resolver(int (Manager::*func)(std::shared_ptr<T>, Args...)) -> decltype(func) {
^
test.cpp:15:6: note: template argument deduction/substitution failed:
test.cpp:29:52: note: mismatched types 'std::shared_ptr<int>' and 'std::shared_ptr<double>'
int result = (ptr->*resolver<int>(&Manager::funcA))(var, 2.0);
^
test.cpp:29:52: note: could not resolve address from overloaded function '& Manager::funcA'
test >
As a workaround, you may use
template <typename T>
struct resolver
{
template <typename... Args>
auto operator ()(int (Manager::*func)(std::shared_ptr<T>, Args...)) -> decltype(func) {
return func;
}
};
With call like
(ptr->*resolver<int>{}(&Manager::funcA))(var, 2.0);
Note the extra {} to call constructor.
Related
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
The following C++20 program:
#include <utility>
#include <cstddef>
template<typename... Args>
class C {
template<size_t... I>
static void call(
std::index_sequence<I...> = std::index_sequence_for<Args...>{}
) {}
};
int main() {
C<long int>::call();
}
fails to compile with error message:
test.cc: In static member function ‘static void C<Args>::call(std::index_sequence<I ...>) [with long unsigned int ...I = {}; Args = {long int}; std::index_sequence<I ...> = std::integer_sequence<long unsigned int>]’:
test.cc:11:20: error: could not convert ‘std::index_sequence_for<long int>{}’ from ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’ to ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’
11 | C<long int>::call();
| ^
| |
| integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>
test.cc:11:20: note: when instantiating default argument for call to ‘static void C<Args>::call(std::index_sequence<I ...>) [with long unsigned int ...I = {}; Args = {long int}; std::index_sequence<I ...> = std::integer_sequence<long unsigned int>]’
test.cc: In function ‘int main()’:
test.cc:11:20: error: could not convert ‘std::index_sequence_for<long int>{}’ from ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’ to ‘integer_sequence<[...],#‘nontype_argument_pack’ not supported by dump_expr#<expression error>>’
Any ideas?
Update:
My current best workaround is to factor out default argument into two functions like:
template<typename... Args>
class C {
static void call() {
_call(std::index_sequence_for<Args...>{});
}
template<size_t... I>
static void _call(std::index_sequence<I...>) {}
};
This seems to work around compiler bug (if that's what it is).
Update 2:
The below program fails for the same reason the original one does:
template<typename T> void f(T x = 42) {}
int main() { f(); }
so it's a feature not a bug.
In general template argument deduction + default function arguments cause a lot of trouble. To simply fix it you can go with this:
#include <utility>
#include <cstddef>
template<typename... Args>
class C {
public:
static void call() {
call_impl(std::index_sequence_for<Args...>{});
}
private:
template<size_t... I>
static void call_impl(std::index_sequence<I...> ) {
}
};
int main() {
C<long int>::call();
}
In C++20 you can also write a templated lambda to do exactly that without creating a new function:
//...
static void call() {
return [&]<size_t... Is>(std::index_sequence<Is...>) {
/* your code goes here... */
}( std::index_sequence_for<Args...>{} );
}
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 */;
Why does callB(f) compile and callA(f) does not compile? Aren't they kind of the same?
I have tried with gcc and MSVC... same results.
#include <functional>
template<typename T>
struct Function
{
typedef std::function<void(T)> type;
};
template<typename T = int>
void callA(std::function<void(T)> f)
{
f(T());
}
template<typename T = int>
void callB(typename Function<T>::type f)
{
f(T());
}
int main()
{
auto f = [](int) { };
callA(f); // compile error: "template argument deduction/substitution failed: main()::<lambda(int)>’ is not derived from ‘std::function<void(T)>"
callB(f); // compiles
return 0;
}
What are the rules for template instantiation when we pass a (multi)derived class to a template function expecting base class? For example:
#include <iostream>
template <int x>
struct C {};
struct D : C<0>, C<1> {};
template <int x>
void f (const C<x> &y) { std::cout << x << "\n"; }
int main ()
{
f (D ());
}
MSVC 2015 prints 0, clang 3.8 - 1 and gcc 6.2 gives compiler error (Demo). And even if you SFINAE-away all overloads except one, the result will still be different:
#include <iostream>
template <int x> struct C {};
template<>
struct C<0> { using type = void; };
struct D : C<0>, C<1> {};
template <int x, typename = typename C<x>::type>
void f (const C<x> &y) { std::cout << x << "\n"; }
int main ()
{
f (D ());
}
Now it compiles only with MSVC, and if you swap C<0> and C<1> only clang will compile it. The problem is that MSVC only tries to instantiate first base, clang - last and gcc prints error too early. Which compiler is right?
gcc 5.4:
/tmp/gcc-explorer-compiler11685-58-1h67lnf/example.cpp: In function 'int main()':
13 : error: no matching function for call to 'f(D)'
f (D ());
^
9 : note: candidate: template<int x> void f(const C<x>&)
void f (const C<x> &y) { std::cout << x << "\n"; }
^
9 : note: template argument deduction/substitution failed:
13 : note: 'const C<x>' is an ambiguous base class of 'D'
f (D ());
^
Compilation failed
Which seems to me to be the correct result, since C<0> and C<1> are equally specialised.
Same result for gcc 6.2
clang 3.8.1 compiles it, which in my view is a compiler bug.
update:
I don't know the actual use case but I was wonder whether this might work for you:
#include <utility>
#include <iostream>
template<class T>
struct has_type
{
template<class U> static auto test(U*) -> decltype(typename U::type{}, std::true_type());
static auto test(...) -> decltype(std::false_type());
using type = decltype(test((T*)0));
static const auto value = type::value;
};
template <int x> struct C {};
template<>
struct C<0> { using type = int; };
template<int...xs>
struct enumerates_C : C<xs>...
{
};
struct D : enumerates_C<0, 1> {};
template<int x, std::enable_if_t<has_type<C<x>>::value>* = nullptr>
void f_impl(const C<x>& y)
{
std::cout << x << "\n";
}
template<int x, std::enable_if_t<not has_type<C<x>>::value>* = nullptr>
void f_impl(const C<x>& y)
{
// do nothing
}
template <int...xs>
void f (const enumerates_C<xs...> &y)
{
using expand = int[];
void(expand { 0,
(f_impl(static_cast<C<xs> const &>(y)),0)...
});
}
int main ()
{
f (D ());
}
expected output (tested on apple clang):
0