I have a function template as in the following minimal snippet. I want to test if it is callable before making the call:
#include <concepts>
void f(auto a) {}
// void f(int a) {} // This is ok
static_assert(std::invocable<decltype(f), int>);
But it does not compile with error
error: 'decltype' cannot resolve address of overloaded function
Alternatively,
void f(auto a) {}
template <auto F> concept callable = requires { {F(27)}; };
static_assert(callable<f>);
gives error
error: unable to deduce 'auto' from 'f'
Is this a limitation of C++20 language? Is there any way to force an instantiation of f<int>? Are there alternatives to make the check compile without changes to f?
A function template can only perform template argument deduction if you call it. So if you're not calling it, the only things you can do with a function template name is give it the template parameters you want (thus resolving into an actual function).
Related
Background
As per [temp.arg.template]/1,
A template-argument for a template template-parameter shall be the
name of a class template or an alias template, expressed as
id-expression.
which means that it is not possible to pass a function template as a default template template argument.
As expected, the following code snippet:
template <typename>
void func() {}
template <template <typename> typename Wrapper = decltype(&func)>
struct Foo {};
results in the error below (Clang):
error: default template argument for a template template parameter must be a class template
template <template <typename> typename Wrapper = decltype(&func)>
Problem
However, when a function template is provided as a default template template argument constrained by concept, this raises a different error:
void placeholder() {}
void func(std::invocable auto f) {}
template <typename Fn, typename FnArg>
concept FooConcept = std::is_invocable_v<FnArg>;
template <FooConcept<decltype(&placeholder)> Wrapper = decltype(&func)>
struct Foo {};
This, surprisingly, yields an overloaded error:
Clang
error: reference to overloaded function could not be resolved; did you mean to call it?
template <FooConcept<decltype(&placeholder)> Wrapper = decltype(&func)>
GCC
error: 'decltype' cannot resolve address of overloaded function
99 | template <FooConcept<decltype(&placeholder)> Wrapper = decltype(&func)>
When decltype(&func) is replaced with a functor, which is a class template,
void placeholder() {}
template <typename Fn, typename FnArg>
concept FooConcept = std::is_invocable_v<FnArg>;
struct Functor {
auto operator()(std::invocable auto f) -> void {}
};
template <FooConcept<decltype(&placeholder)> Wrapper = Functor>
struct Foo {};
This compiles without an error.
Question
Is this derived from the error in Background but showing a different message?
If not, how come it shows an error related to an overloaded function?
Is this derived from the error in Background but showing a different message?
No, it's totally unrelated. The background error is trying to pass in a type as a default argument for a parameter expecting a template: it's just the wrong kind of argument.
If not, how come it shows an error related to an overloaded function?
What you're trying to do reduces to:
void func(auto f) {}
using T = decltype(&func); // error
There is no one type of func - func isn't a function, it's a function template. It can be used to instantiate many different functions which have many different types. There's not enough information here to pick which overload that is being selected.
You'd have to manually choose, as in:
using T = decltype(static_cast<void(*)(int)>(func)); // ok
See also this question.
The fact that the parameter that decltype(&func) is intended to be a default argument for is constrained by invocable doesn't matter - that information doesn't participate in the overload resolution necessary to pick which func is the desired one.
Providing Functor as a default argument works fine because that is already a type, so it just works.
Consider the following code
template<bool b, typename T> void foo(const T& t = []() {}) {
// implementation here
}
void bar() {
foo<true>([&](){ /* implementation here */ }); // this compiles
foo<true>(); // this doesn't compile
}
In the case that doesn't compile I get the following errors:
error C2672: 'foo': no matching overloaded function found
error C2783: 'void foo(const T&)': could not deduce template argument for 'T'
I think it's clear what I want to achieve: let foo be called with and without a client-provided lambda. The compiler is MSVC++2017 version 15.4.4 toolset v141.
Default function arguments are not part of the template argument deduction process. To quote [temp.deduct.partial]/3:
The types used to determine the ordering depend on the context in
which the partial ordering is done:
In the context of a function call, the types used are those function parameter types for which the function call has arguments.
141
141) Default arguments are not considered to be arguments in this
context; they only become arguments after a function has been
selected.
That bullet and note indicate that since you didn't provide an argument for t in the call to foo, the type T cannot be deduced. The default lambda argument can only be taken into account if the function is selected to be called, not before.
The solution, as all the others have noted, is to provide an overload without parameters, that will call the templated one with the default lambda you have in mind.
Another (very efficient) way - default T to be a null functor.
// no_op is a function object which does nothing, regardless of how many
// arguments you give it. It will be elided completely unless you compile with
// -O0
struct no_op
{
template<class...Args>
constexpr void operator()(Args&&...) const {}
};
// foo defaults to using a default-constructed no_op as its function object
template<bool b, typename T = no_op> void foo(T&& t = T())
{
// implementation here
t();
}
void bar() {
foo<true>([&](){ std::cout << "something\n"; }); // this compiles
foo<true>(); // this now compiles
}
The compiler uses the arguments passed to deduce the template type. If there's no arguments, then how would the compiler be able to deduce the template type?
You can use overloading instead of default arguments here.
The overloaded non-argument function can simply call the function with the "default" argument:
template<bool b, typename T> void foo(const T& t) {
// implementation here
}
template<bool b> void foo() {
foo<b>([]() {});
}
Consider overloading it directly:
template <bool b>
void foo(void) {
foo([](){});
}
See CppReference:
Non-deduced contexts
4) A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done:
Type template parameter cannot be deduced from the type of a function default argument:
template void f(T = 5, T = 7);
void g()
{
f(1); // OK: calls f<int>(1, 7)
f(); // error: cannot deduce T
f<int>(); // OK: calls f<int>(5, 7)
}
You are trying to say something that makes no sense. You are asking the compiler to guess T from your arguments, but then you do not provide any argument.
The following code does compile, and does what you want:
template<bool b, typename T> void foo(const T& t) {
// implementation here
}
template<bool b> void foo() {
foo<b>([]() {}); // Call actual implementation with empty lambda
}
void bar() {
foo<true>([&](){ /* implementation here */ }); // this compiles
foo<true>(); // this now compiles as well
}
I'm experimenting with resolving the address of an overloaded function (bar) in the context of another function's parameter (foo1/foo2).
struct Baz {};
int bar() { return 0; }
float bar(int) { return 0.0f; }
void bar(Baz *) {}
void foo1(void (&)(Baz *)) {}
template <class T, class D>
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}
int main() {
foo1(bar); // Works
foo2<Baz>(bar); // Fails
}
There's no trouble with foo1, which specifies bar's type explicitly.
However, foo2, which disable itself via SFINAE for all but one version of bar, fails to compile with the following message :
main.cpp:19:5: fatal error: no matching function for call to 'foo2'
foo2<Baz>(bar); // Fails
^~~~~~~~~
main.cpp:15:6: note: candidate template ignored: couldn't infer template argument 'D'
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}
^
1 error generated.
It is my understanding that C++ cannot resolve the overloaded function's address and perform template argument deduction at the same time.
Is that the cause ? Is there a way to make foo2<Baz>(bar); (or something similar) compile ?
As mentioned in the comments, [14.8.2.1/6] (working draft, deducing template arguments from a function call) rules in this case (emphasis mine):
When P is a function type, function pointer type, or pointer to member function type:
If the argument is an overload set containing one or more function templates, the parameter is treated as a non-deduced context.
If the argument is an overload set (not containing function templates), trial argument deduction is attempted using each of the members of the set. If deduction succeeds for only one of the overload set members, that member is used as the argument value for the deduction. If deduction succeeds for more than one member of the overload set the parameter is treated as a non-deduced context.
SFINAE takes its part to the game once the deduction is over, so it doesn't help to work around the standard's rules.
For further details, you can see the examples at the end of the bullet linked above.
About your last question:
Is there a way to make foo2<Baz>(bar); (or something similar) compile ?
Two possible alternatives:
If you don't want to modify the definition of foo2, you can invoke it as:
foo2<Baz>(static_cast<void(*)(Baz *)>(bar));
This way you explicitly pick a function out of the overload set.
If modifying foo2 is allowed, you can rewrite it as:
template <class T, class R>
auto foo2(R(*d)(T*)) {}
It's more or less what you had before, no decltype in this case and a return type you can freely ignore.
Actually you don't need to use any SFINAE'd function to do that, deduction is enough.
In this case foo2<Baz>(bar); is correctly resolved.
Some kind of the general answer is here: Expression SFINAE to overload on type of passed function pointer
For the practical case, there's no need to use type traits or decltype() - the good old overload resolution will select the most appropriate function for you and break it into 'arguments' and 'return type'. Just enumerate all possible calling conventions
// Common functions
template <class T, typename R> void foo2(R(*)(T*)) {}
// Different calling conventions
#ifdef _W64
template <class T, typename R> void foo2(R(__vectorcall *)(T*)) {}
#else
template <class T, typename R> void foo2(R(__stdcall *)(T*)) {}
#endif
// Lambdas
template <class T, class D>
auto foo2(const D &d) -> void_t<decltype(d(std::declval<T*>()))> {}
It could be useful to wrap them in a templated structure
template<typename... T>
struct Foo2 {
// Common functions
template <typename R> static void foo2(R(*)(T*...)) {}
...
};
Zoo2<Baz>::foo2(bar);
Although, it will require more code for member functions as they have modifiers (const, volatile, &&)
ideone example
I need to forward some predefined arguments plus some user-passed arguments to a member function.
#define FWD(xs) ::std::forward<decltype(xs)>(xs)
template<class T, class... Ts, class... TArgs>
void forwarder(void(T::*fptr)(Ts...), TArgs&&... xs)
{
T instance;
(instance.*fptr)(FWD(xs)..., 0);
// ^
// example predefined argument
}
forwarder(&example::f0, 10, 'a');
forwarder(&example::f1, 10, "hello", 5);
This works properly for non-template member functions.
The member function pointer passed to forwarder can, however, point to template functions as well. Unfortunately, the compiler is not able to deduce the type of T in that case:
struct example
{
void f0(int, int) { }
template<class T>
void f1(T&&, int) { }
};
// Compiles
forwarder(&example::f0, 10);
// Does not compile
forwarder(&example::f1, 10);
Errors:
prog.cpp:30:28: error: no matching function for call to 'forwarder(<unresolved overloaded function type>, int)'
forwarder(&example::f1, 10);
^
prog.cpp:20:6: note: candidate: template<class T, class ... Ts, class ... TArgs> void forwarder(void (T::*)(Ts ...), TArgs&& ...)
void forwarder(void(T::*fptr)(Ts...), TArgs&&... xs)
^
prog.cpp:20:6: note: template argument deduction/substitution failed:
prog.cpp:30:28: note: couldn't deduce template parameter 'T'
forwarder(&example::f1, 10);
Is there any way I can help the compiler deduce the correct types without changing the interface of forwarder?
If not, what's the best way of solving this issue without making the user syntax too convoluted?
EDIT: It would also be acceptable to pass the member function pointer as a template parameter, maybe through a wrapper. The target member function will always be known at compile-time. Pseudocode:
forwarder<WRAP<example::f0>>(10, 'a');
// Where WRAP can be a macro or a type alias.
ideone example
I compiled your code in gcc 4.9 by providing template arguments to the member function pointer;
like this
int main(){
// Compiles
forwarder(&example::f0, 10);
//Does not compile
forwarder(&example::f1, 10);
//Does compile, instantiate template with int or what ever you need
forwarder(&example::f1<int>,10)
}
I believe what is going on is that you need to instantiate the template member function.
does that change your interface too much?
I think any answer will revolve around instantiating that member template somehow.
I need to override connection between boost::signals2::signal and boost::function.
For this purpose I've created following template function:
template<typename T>
void bind(boost::signals2::signal<T> &signal, boost::function<T> function) {
// override code ...
}
I want to make use of this bind as simple as it can be.
From what I've read in posts on similar issues, template parameter should be deduced from function arguments.
But in my case when there's no explicit parameter it's not working.
boost::signals2::signal<void ()> my_signal;
bind<void ()>(my_signal, boost::bind(&A::func, this)); // this works
bind(my_signal, boost::bind(&A::func, this)); // error: no matching function for call
Am I missing something?
Can there be any workaround to avoid explicit template parameter?
The second argument type is not std::function<T>, but some bind type, so the compiler is unable to deduce the T for the second function parameter. You need to tell the compiler "You are OK with not finding a type for T in the second function parameter". This can be done by making the second parameter a non-deduced context.
template<typename T>
void bind(boost::signals2::signal<T> &signal,
typename std::common_type<boost::function<T>>::type function) {
// override code ...
}