This question already has an answer here:
Function overloading: empty parameter list vs parameter pack
(1 answer)
Closed 8 months ago.
Consider this piece of code:
template<typename FirstArg>
void foo()
{
}
template<typename FirstArg, typename... RestOfArgs>
void foo()
{
foo<RestOfArgs...>();
}
int main()
{
foo<int, int, int>();
return 0;
}
It does not compile due to ambiguous call foo<RestOfArgs...>(); when RestOfArgs has only one element ({int}).
But this compiles without error:
template<typename FirstArg>
void foo(FirstArg x)
{
}
template<typename FirstArg, typename... RestOfArgs>
void foo(FirstArg x, RestOfArgs... y)
{
foo(y...);
}
int main()
{
foo<int, int, int>(5, 6, 7);
return 0;
}
Why is there ambiguity in the first case?
Why is there no ambiguity in the second case?
In Function template overloading
There is lot of rules to tell which template function is more specialized (according to given parameters).
The point which makes
template<typename> void foo();
template<typename, typename...> void foo();
ambiguous for foo<int>(), but not
template<typename T> void foo(T);
template<typename T, typename... Ts> void foo(T, Ts...);
for foo(42) is the following:
In case of a tie, if one function template has a trailing parameter pack and the other does not, the one with the omitted parameter is considered to be more specialized than the one with the empty parameter pack.
The answer by #ZangMingJie answers the difference in behavior your are observing in your code.
I found it easier to understand the name resolution with the following change:
template<typename FirstArg>
void foo()
{
printf("1\n");
}
template<typename FirstArg, typename SecondArg, typename... RestOfArgs>
void foo()
{
printf("2\n");
foo<SecondArg, RestOfArgs...>();
}
int main()
{
foo<int, int, int>();
return 0;
}
When two or more template parameters are used, the second function gets invoked. When one template parameter is used, the first function gets invoked.
Why is there ambiguity in the first case?
RestOfArgs can be empty.
So foo<int> can be instantiated as:
template<int>
void foo()
{
}
and
template<int,>
void foo()
{
foo<>();
}
both will compile, so it is ambiguous.
Actually foo<>() won't compile, but it fails in the next instantiation, so it doesn't matter.
Why is there no ambiguity in the second case?
foo<int>(7) can be instantiated as:
template<int>
void foo(int 7)
{
}
and
template<int>
void foo(int 7)
{
foo();
}
but the second one is an error, because there are no foo taking no argument, so the only candidate is the first one, so there won't be ambiguous
Related
Often times I will have a templated function where I try to pass a && type. Problem is, if I put std::move as the argument, I get an error such as this one:
error: no matching function for call to 'doThing(void (*)(int&&), std::remove_reference<int&>::type)'
The code that generated that particular error is as following:
#include <utility>
template<typename T>
void doThing(void (*thing)(T), T input)
{
thing(input);
}
void exampleThing(int&& someData)
{someData++;}
int main()
{
int x, y = 5;
exampleThing(std::move(x)); //compiles fine
doThing(&exampleThing, std::move(y)); //error as shown above
}
So, how would I pass an argument to a template as a move?
Issue with
template<typename T>
void doThing(void (*thing)(T), T input)
is that T is deduced from 2 places, and should be identical.
Simpler to split in 2 template parameters:
template <typename Arg, typename T>
void doThing(void (*thing)(Arg), T&& input)
{
thing(std::forward<T>(input));
}
Demo
Another option is to allow deduction only at one place:
I use std::type_identity (C++20) for this, but can be trivially re-implemented for previous version.
template <typename Arg>
void doThing(void (*thing)(Arg), std::type_identity_t<Arg> input)
{
thing(std::forward<Arg>(input));
}
Demo
Why does the following code not compile under either gcc or clang:
class Foo {
public:
void bar(int) {}
};
template< class T, typename ...Args, void(T::*Member)(Args...) >
void member_dispatch(Args&&... args, void* userdata)
{
T* obj = static_cast<T*>(userdata);
(obj->*Member)(std::forward<Args>(args)...);
}
int main()
{
Foo foo;
member_dispatch<Foo, int, &Foo::bar>(1, &foo);
return 0;
}
See e.g. here.
This question can possibly be merged with this one, though here I get unclear compilation errors from gcc and clang (instead of VS).
When you explicitly specify the types, a parameter pack is greedy. &Foo::bar will be parsed as part of typename ...Args, which causes the error.
The correct way to write this is to put it in the function parameter list, instead of a non-type template parameter.
template< class T, typename ...Args>
void member_dispatch(Args&&... args, void(T::*Member)(Args...), void* userdata)
{
T* obj = static_cast<T*>(userdata);
(obj->*Member)(std::forward<Args>(args)...);
}
int main()
{
Foo foo;
member_dispatch<Foo, int>(1, &Foo::bar, &foo);
return 0;
}
A Better Way:
It would be better to take advantage of C++'s template argument deduction. But here you doesn't put the parameter pact at the end of a function parameter list, which is a non-deduced context. So I suggest you re-order it, so that you don't need to specify the template argument:
template<class T, class K, typename ...Args>
void member_dispatch(K T::*ptr, void* userdata, Args&&... args)
{
T* obj = static_cast<T*>(userdata);
(obj->*ptr)(std::forward<Args>(args)...);
}
int main()
{
Foo foo;
member_dispatch(&Foo::bar, &foo, 1);
return 0;
}
Try using a class template instead of a function template.
There has also been a std syntax proposal "template << auto x >> " for none-type template parameters. Proper implementation will simplify your library syntax.
enum class enabler{};
template<typename T>
class X {
template<typename std::enable_if<std::is_class<T>::value,enabler>::type = enabler()>
void func();
void func(int a);
void func(std::string b);
};
I have this class with these 3 overloads for func. I need the second/third versions to be available for both class/non-class types, and the first version to be available only for class types. when I tried to use enable_if as above, the class instantiation for non-class types gives compile error.
For SFINAE to work, the template argument must be deduced. In your case, T is already known by the time you attempt to instantiate func, so if the enable_if condition is false, instead of SFINAE, you get a hard error.
To fix the error, just add a template parameter whose default value is T, and use this new parameter in the enable_if check. Now deduction occurs and SFINAE can kick in for non-class types.
template<typename U = T,
typename std::enable_if<std::is_class<U>::value,enabler>::type = enabler()>
void func();
And you don't really need a dedicated enabler type either, this works too
template<typename U = T,
typename std::enable_if<std::is_class<U>::value, int>::type* = nullptr>
void func();
I'm not really sure what you're going for with enabler here, but you can't do what you're trying because the declaration for your member function must be valid since T is not deduced by func. To achieve what you want in adding an extra overload, you can use some moderately contrived inheritance.
struct XBaseImpl {
// whatever you want in both versions
void func(int a) { }
void func(std::string b) { }
};
template <typename, bool> struct XBase;
// is_class is true, contains the extra overload you want
template <typename T>
struct XBase<T, true> : XBaseImpl {
static_assert(std::is_class<T>{}, ""); // just to be safe
using XBaseImpl::func;
void func() { } // class-only
};
// is_class is false
template <typename T>
struct XBase<T, false> : XBaseImpl { };
template<typename T>
class X : public XBase<T, std::is_class<T>{}> { };
You are not enabling or disabling something.
You simply want a compile time error in one specific case.
Because of that you don't require to rely on sfinae, a static_assert is enough.
As a minimal, working example:
#include<string>
template<typename T>
class X {
public:
void func() {
static_assert(std::is_class<T>::value, "!");
// do whatever you want here
}
void func(int a) {}
void func(std::string b) {}
};
int main() {
X<int> x1;
X<std::string> x2;
x2.func(42);
x2.func();
x1.func(42);
// compilation error
// x1.func();
}
Once a SO user said me: this is not sfinae, this is - substitution failure is always an error - and in this case you should use a static_assert instead.
He was right, as shown in the above example a static_assert is easier to write and to understand than sfinae and does its work as well.
There is a function
template <class ...T>
void foo(std::function<void(T...)> callback);
into which I pass a callback.
I'd like to do something like
foo(bar);
where bar is, for example,
void bar(int a, long b, double c, float d);
but that gives me
error: no matching function for call to bar(void (&)(int, long int, double, float))
I have to call foo as
foo(std::function<void(int, long, double, float)>(bar));
which is too verbose. Even
foo<int, long, double, float>(bar);
would have been better.
foo(bar);
would be just ideal.
Anyway, how can I make calls to foo to be less verbose?
Edit: declaration of foo has to stay the same.
I'd write a wrapper function that translates the function pointer into a std::function wrapper:
template <typename... T>
void foo(std::function<void (T...)> f) {}
template <typename... T>
void foo(void (*f)(T...)) {
foo(std::function<void (T...)>(f));
}
foo() can then be called either way:
void bar(int,double) {}
void foo_caller() {
foo(std::function<void (int,double)>(bar));
foo(bar);
}
Addendum: Non-static member function wrapper
Same approach can be used for pointer-to-member functions — just add another overload:
template <typename C,typename... T>
void foo(void (C::*f)(T...)) {
foo(std::function<void (C *,T...)>(f));
}
Note the extra first parameter for the this pointer for the member function. Usage is similar:
struct quux {
void mf(char *,double) {}
};
void foo_caller() {
foo(&quux::mf);
}
If you know you will pass a plain function pointer to foo, and not just any C++11 lambda, you can redefine foo as:
template <class ...T>
void foo(void(*callback)(T...)) {
// .....
}
If you want to support lambdas, you can be more generic with the type
template <class LambdaType>
void foo(LambdaType callback) {
// .....
}
the downside of this approach is that if you pass something that is not a function or lambda, you will get weird template error messages coming from inside of foo.
With your original solution the compiler has problems matching T... to int, long, double, float, probably because it is a nested type.
If I told you to match void(int, double) to MyTempalte<T...> you wouldn't know that I intend to replace T... with int, double, because you don't know what MyTemplate does with its arguments. Maybe MyTemplate is doing something weird to its template arguments first?
Same, the compiler doesn't know how to match std::function template parameters to your function pointer.
In case your foo definition is not set in stone, can change it to
#include <functional>
template <class Ret, class ...T>
void foo(Ret callback(T... params))
{
}
void bar(int a, long b, double c, float d){}
int main()
{
foo(bar);
}
In this example a function is passed to an implicitly instantiated function template.
// Function that will be passed as argument
int foo() { return 0; }
// Function template to call passed function
template<typename F>
int call(F f) {
return f();
}
template<typename F, typename A>
int call(F f, A a) {
return f(a);
}
int a = call(foo);
We can break this code by adding an overload for foo().
int foo(int i) { return 0; }
The name "foo" is now ambiguous and the example will no longer compile. This can be made to compile by explicitly providing function pointer type info.
int (*func_takes_void)() = foo;
int a = call(func_takes_void);
int (*func_takes_int)(int) = foo;
int b = call(func_takes_int, 0);
http://coliru.stacked-crooked.com/a/e08caf6a0ac1e6b9
Is it possible to instead deduce the function pointer types? If so, why does my attempt below not work and what is the right way to do this?
If this is not possible, a good answer would explain why.
Attempt thus far
A human can see which foo() is intended in the two calls by inspecting the definitions of call<>() but that info is not available to the compiler for overload resolution. Still, the information is all there, it just needs to be pulled into the function template signature. This may be possible with expression SFINAE.
In pseudo code we want this:
template<IgnoreThis, typename ReturnType>
struct expr_check
{
typedef ReturnType type;
}
template<typename F>
expr_check<expression requiring F have correct signature, result_of<F>::type>::type
call(F f);
Here is that idea worked out in real code.
http://coliru.stacked-crooked.com/a/a3ce828d6cb16c2d
The function template signatures are:
template<typename F>
typename expr_check<sizeof(declval<F>()()), typename func_ptr_result<F>::type>::type
call(F f);
template<typename F, typename A>
typename expr_check<sizeof(declval<F>()(declval<A>())), typename func_ptr_result<F>::type>::type
call(F f, A a);
What I currently have does not compile. From the compiler output you can see that on both attempts to instantiate the function template there is substitution failure on one call<>() overload and the other simply gives an opaque "couldn't deduce template parameter".
(The colirus were compiled as C++03 but C++11 answers are fine.)
My suspicion is that while instantiating call<>(), foo() is not being called and C++ simply does not provide for overload resolution of foo() in this context. It doesn't matter that it can be proven that one foo() overload is the correct one, C++ just doesn't mandate overload resolution here. On the other hand, overload resolution isn't limited to a function being called. A function pointer of appropriate type gets to select overloads of foo().
Related questions
There are a few questions asking about overloading on function pointer type. It looks like this can't be done. I didn't find any questions trying to do this through expression SFINAE.
This seems to be the closest related question.
Is there a way to deduce the value of a function pointer template parameter?
Bonus pedantry
Is "function pointer" the correct phrase to have used in the title? Would "function reference" have been more accurate?
The closest you can get is probably this:
struct sfoo
{
template<typename... args>
void operator() (args&&... a)
{
foo(std::forward<args>(a)...);
}
};
and pass sfoo (or sfoo()) instead of foo around.
That is, create a function object type that encapsulates the entire overload set in the templatized operator().
Then instead of overload resolution over a template argument, which does not exist, you get a template instantiation over the same argument, which is OK.
As has been mentioned before, SFINAE doesn't work because the names of overloaded functions have no definite type in C++, therefore template parameter substitution doesn't even happen at this stage.
However, in your example, the problem is arguably not that you have too many overloads of "foo", but too few overloads of "call". Just provide both the templates with typename F and the ones that expect a function pointer. The compiler will now be able to do the right thing depending on context:
#include <iostream>
// Functions
int foo() { return 0; }
int foo(int) { return 1; }
// Function object
struct Foo
{
int operator()() const { return 2; }
int operator()(int) const { return 3; }
};
// Higher-order functions / templates
template<typename F>
int call(F f) {
return f();
}
int call(int (*f)()) {
return f();
}
template<typename F, typename A>
int call(F f, A a) {
return f(a);
}
template<typename A>
int call(int (*f)(A), A a) {
return f(a);
}
int main()
{
int a = call(foo)
, b = call(foo, 0)
, c = call(Foo())
, d = call(Foo(), 0);
std::cout << a << ',' << b << ',' << c << ',' << d << '\n'; // 0,1,2,3
}
The call overloads can be made more generic by adding return type deduction. In C++11, this is possible even with function objects by using decltype rsp. result_of. For brevity, I will post only the new function signatures, as the bodies don't need to be changed in this case:
template<typename F>
auto call(F f) -> decltype(f());
template<typename R>
R call(R (*f)());
template<typename F, typename A>
auto call(F f, A a) -> decltype(f(a));
template<typename R, typename A>
R call(R (*f)(A), A a);