i want to use recursive variadic templates with a base case with more than 2 types. The following example does not compile. What could be the problem?
My expectation was that f<int, int>(5) would call a case like:
[with T = int; Arg = int; Args = {int}]
but that seems to be no option for the compiler (g++ c++17) ;)
template<class T, typename Arg, typename... Args>
void f(T a) {
}
template<class T, typename Arg>
void f (T a) {
}
int main() {
f<int, int>(5);
return 0;
}
<source>: In function 'int main()':
<source>:11:18: error: call of overloaded 'f<int, int>(int)' is ambiguous
11 | f<int, int>(5);
| ^
<source>:2:6: note: candidate: 'void f(T) [with T = int; Arg = int; Args = {}]'
2 | void f(T a) {
| ^
<source>:6:6: note: candidate: 'void f(T) [with T = int; Arg = int]'
6 | void f (T a) {
| ^
Compiler returned: 1
You can combine both cases and use a constexpr if statement in the function template:
#include <iostream>
template<class T, typename Arg, typename... Args>
void f(T a) {
std::cout << sizeof...(Args) << '\n';
if constexpr (sizeof...(Args) > 0) {
f<T, Args...>(a);
}
}
int main() {
f<int, int>(5);
std::cout << "---\n";
f<int, int, double, float, int>(5);
}
Output:
0
---
3
2
1
0
Related
I'm writing a class that wraps a member function while allowing for type-erasure.
#include <utility>
template<typename T>
class FunctionWrapper;
template<typename Ret, typename...Args>
class FunctionWrapper<Ret(Args...)> {
public:
template<typename T>
FunctionWrapper(T& object_reference, Ret(T::*function_ref)(Args...)) :
object_ptr(&object_reference), callable(&static_method<T, function_ref>) {}
private:
typedef Ret (*callable_type)(void*, Args...);
void* object_ptr;
callable_type callable;
Ret operator()(Args&&...args) {
callable(object_ptr, std::forward<Args>(args)...);
}
template<typename T, Ret(T::*function_ref)(Args...)>
Ret static static_method(void* object_ptr, Args...args) {
T* obj = static_cast<T*>(object_ptr);
return (obj->*function_ref)(args...);
}
};
struct S {
void add() {}
};
template<typename T, typename Ret, typename... Args>
auto static get_function_wrapper(T& object_ref, Ret(T::*function_ref)(Args...)) {
return FunctionWrapper<Ret(Args...)>(object_ref, function_ref);
}
int main() {
S s;
const auto shallow_function = get_function_wrapper(s, &S::add);
}
But I'm receiving an error saying that the deduced static_method is not a callable_type which I don't understand.
<source>: In instantiation of 'FunctionWrapper<Ret(Args ...)>::FunctionWrapper(T&, Ret (T::*)(Args ...)) [with T = S; Ret = void; Args = {}]':
<source>:38:12: required from 'auto get_function_wrapper(T&, Ret (T::*)(Args ...)) [with T = S; Ret = void; Args = {}]'
<source>:43:55: required from here
<source>:12:40: error: no matches converting function 'static_method' to type 'FunctionWrapper<void()>::callable_type' {aka 'void (*)(void*)'}
12 | object_ptr(&object_reference), callable(&static_method<T, function_ref>) {}
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:25:17: note: candidate is: 'template<class T, void (T::* function_ref)()> static Ret FunctionWrapper<Ret(Args ...)>::static_method(void*, Args ...) [with Ret (T::* function_ref)(Args ...) = T; Ret = void; Args = {}]'
25 | Ret static static_method(void* object_ptr, Args...args) {
| ^~~~~~~~~~~~~
Execution build compiler returned: 1
I don't understand why this isn't working
I'm taking a templated rvalue-reference in the function signature which should make it an universal reference. However, lvalues are still not able to bind to this parameter. Check this out:
Demo
#include <concepts>
#include <cstdio>
template <typename T = int>
struct some_struct
{
some_struct(T a)
: a_{ a }
{ }
auto print() {
printf("hello %d\n", a_);
}
T a_ = 0;
};
/* this doesnt work
template <typename T>
auto foo(some_struct<T>&& arg) {
if constexpr (std::same_as<T, int>) {
arg.print();
}
}
template <typename... Ts>
auto bar(some_struct<Ts>&&... args) {
([&]{
if constexpr (std::same_as<Ts, int>) {
args.print();
}
}(), ...);
}
*/
template <typename T>
auto foo(some_struct<T>& arg) {
if constexpr (std::same_as<T, int>) {
arg.print();
}
}
template <typename... Ts>
auto bar(some_struct<Ts>&... args) {
([&]{
if constexpr (std::same_as<Ts, int>) {
args.print();
}
}(), ...);
}
int main() {
some_struct first{1};
some_struct second{2};
some_struct third{'c'};
some_struct fourth{true};
foo(first);
bar(second,third,fourth);
}
Yields:
<source>:58:9: error: cannot bind rvalue reference of type 'some_struct<int>&&' to lvalue of type 'some_struct<int>'
58 | foo(first);
| ^~~~~
<source>:19:27: note: initializing argument 1 of 'auto foo(some_struct<T>&&) [with T = int]'
19 | auto foo(some_struct<T>&& arg) {
| ~~~~~~~~~~~~~~~~~^~~
<source>:60:9: error: cannot bind rvalue reference of type 'some_struct<int>&&' to lvalue of type 'some_struct<int>'
60 | bar(second,third,fourth);
| ^~~~~~
<source>:26:27: note: initializing argument 1 of 'auto bar(some_struct<Ts>&& ...) [with Ts = {int, char, bool}]'
26 | auto bar(some_struct<Ts>&&... args) {
I am not able to figure out why following code does not compile. In the overload 1, the type deduced is int & so, it should result in SFINAE. And we should only have one candidate function.
#include <iostream>
#include <type_traits>
template <typename T>
std::enable_if<std::is_same<T, std::remove_reference_t<T>>::value, void> foo(T&& a){
std::cout << "overload 1" << std::endl;
}
template <typename T>
void foo(T&& a){
std::cout << std::is_same<T, std::remove_reference_t<T>>::value << std::endl;
std::cout << "overload 2" << std::endl;
}
int main(){
int a = 2;
foo(a);
// std::cout << std::is_same<int, int&>::value << std::endl; ---> gives false
return 0;
}
Compilation Error:
template_with_reference.cpp: In function ‘int main()’:
template_with_reference.cpp:19:8: error: call of overloaded ‘foo(int&)’ is ambiguous
foo(a);
^
template_with_reference.cpp:5:74: note: candidate: std::enable_if<std::is_same<T, typename std::remove_reference< <template-parameter-1-1> >::type>::value, void> foo(T&&) [with T = int&; typename std::remove_reference< <template-parameter-1-1> >::type = int]
std::enable_if<std::is_same<T, std::remove_reference_t<T>>::value, void> foo(T&& a){
^~~
template_with_reference.cpp:11:6: note: candidate: void foo(T&&) [with T = int&]
Could you guys help me find the error?
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 */;
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).