This code works perfectly fine with gcc/g++ and msvc but not with clang.
It keeps complaining that no matching function for Log was found, what is going on?
#include <iostream>
template <typename Function, typename... Args>
auto Call(Function func, Args&&... args) -> typename std::result_of<Function&(Args&&...)>::type
{
return func(std::forward<Args>(args)...);
}
template <typename T, typename... Args>
T (*Log( T (*FuncPtr)(Args...) ))(Args...)
{
return FuncPtr;
}
int main()
{
auto r = Log(Call<int(int), int>)([](int x){
return x*10;
}, 10);
std::cerr << r << std::endl;
}
Error:
> error: no matching function for call to 'Log'
> auto r = Log(Call<int(int), int>)([](int x){
> ^~~ test7.cpp:15:5: note: candidate template ignored: couldn't infer template argument 'T' T (*Log( T (*FuncPtr)(Args...)
> ))(Args...)
> ^ 1 error generated.
I believe that this code is incorrect. The function parameter to Log cannot be used for template argument deduction in this case because the argument is a non-deduced context.
From [temp.deduct.type] in the standard, p5 lists the non-deduced contexts, and p5.5 says:
A function parameter for which argument deduction cannot be done
because the associated function argument is a function, or a set of
overloaded functions (13.4), and one or more of the following apply:
and p5.5.3 says:
the set of functions supplied as an argument contains one or more
function templates.
My interpretation is that you have a function parameter for which the function argument is a (pointer to) a function and that function is a function template.
Arguably, because this isn't an overload set, this might be something that could be allowed in the future, but I read the standard as not guaranteeing that this technique will work.
Related
I am trying to implement a function template ovl such that ovl<Foo, Bar>(f) will return the overload of f taking (Foo, Bar), and very surprised with what happens with my naïve solution:
template <class... Args, class Ret>
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Args>...)) { return f; }
void foo();
void foo(int);
void foo(int, int);
int main() {
ovl<int>(foo)(0);
}
prog.cc:26:5: fatal error: no matching function for call to 'ovl'
ovl<int>(foo)(0);
^~~~~~~~
prog.cc:6:16: note: candidate template ignored: couldn't infer template argument 'Ret'
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Args>...)) { return f; }
^
The same error appears with GCC and Clang. What's more, it actually works when enumerating possible arities myself:
template <class Ret>
constexpr auto ovl(Ret (*const f)()) { return f; }
template <class Arg0, class Ret>
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Arg0>)) { return f; }
template <class Arg0, class Arg1, class Ret>
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Arg0>, std::type_identity_t<Arg1>)) { return f; }
// ... ad nauseam.
Wandbox demo
Interestingly, keeping Args... but hardcoding the return type works as well:
template <class... Args>
constexpr auto ovl(void (*const f)(std::type_identity_t<Args>...)) { return f; }
It seems like partial explicit arguments are ignored when they are provided to a parameter pack, but why? And how can I ensure that they are considered when trying to disambiguate the function pointer?
Note: I have found the following workaround which bakes Args... first before deducing Ret, but am still interested in an answer as this is quite clunky.
template <class... Args>
struct ovl_t {
template <class Ret>
constexpr auto operator()(Ret (*const f)(Args...)) const { return f; }
};
template <class... Args>
constexpr ovl_t<Args...> ovl;
I believe this is a compiler bug.
As #StoryTeller - Unslander Monica mentioned [temp.arg.explicit]
Template argument deduction can extend the sequence of template arguments corresponding to a template parameter pack, even when the sequence contains explicitly specified template arguments.
Which means even though we supplied an int argument, the compiler will attempt to deduce more arguments for Args.
However [temp.deduct.call]
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.
The trial deductions are
template<typename... Args, typename Ret>
void trial(Ret(*)(std::type_identity_t<Args>...));
trial<int>((void(*)())foo); // fails
trial<int>((void(*)(int))foo); // succeeds
trial<int>((void(*)(int, int))foo); // fails, trailing Args is non-deduced context
Implying that only the void(int) member is used as the argument value, which will succeed in deducing Ret = void and Args = {int}.
Consider the following snippet (available on compiler epxlorer):
template<typename T, typename... Args>
auto foo(Args&&... args) {}
template<typename... Args>
auto foo(Args&&... args) {}
int main() {
foo<char>('a');
}
It compiles perfectly fine for GCC and fails for both Clang and MSVC (with compiler saying ambiguous call)
Why do Clang and MSVC fail to such seemingly obvious deduction?
EDIT: GCC provides me with the expected solution as a user, is there an easy way to push clang and msvc to choose the template without much change of the original code?
If you examine the additional diagnostic lines from the compiler, you'll see that it says
<source>(6): note: could be 'auto foo<char>(char &&)'
<source>(3): note: or 'auto foo<char,char>(char &&)'
(from MSVC; Clang is similar)
In this case, since the first (only) parameter to the function foo is a char, the compiler cannot distinguish between the one template parameter and two template parameter versions of the template.
If you change your function call to
foo<char>(10);
it will compile.
There is an example in the language spec ("Partial ordering of function templates", [temp.func.order]) very similar to your code:
template<class T, class... U> void f(T, U...); // #1
template<class T > void f(T); // #2
void h(int i) {
f(&i); // error: ambiguous
}
Since GCC compiles it, this is a bug in GCC.
After some tests, and using the mentioned reference to the standard: [temp.func.order], [temp.deduct.partial], I came to the following understanding of the situation.
Problem
Considering the example given in the question:
template<typename T, typename... Args> auto foo(Args&&... args) {} //#1
template<typename... Args> auto foo(Args&&... args) {} //#2
#2 is a function with a variadic parameter pack that can be deduced. can be deduced, not have to. Thus, nothing prevents the user to explicitly specify the template arguments.
Therefore, foo<char>('a') can be as much an explicit instantiation of #2 as an instantiation of #1, provoking the ambiguity. The standard does not favor a preferred match between the overload #1 and #2.
GCC went beyond the standard within its implementation by attributing a higher preference for #1 when a template argument is manually given, while Clang and MSVC kept it vanilla.
Furthermore, ambiguity appears only when the first arguments from the variadic pack and T resolve to the exact same type.
Solution
Here are the solutions that I found for my use case. (Forward object construction or a variadic pack of objects)
Variant 1
Declare an extra function specializing for one argument, this would take precedence over the variadic-based ones. (Does not scale or generalize)
template<typename T> auto foo(T&& args) {}
//or
template<typename T, typename Arg> auto foo(Arg&& arg) {}
Variant 2
Disable the overload when the first argument of the non-empty parameter pack is same as the given type T.
template<typename T, typename... Args>
constexpr bool is_valid() {
if constexpr(sizeof...(Args)==0)
return true;
else
return !std::is_same_v<T,std::tuple_element_t<0,std::tuple<Args...> > > ;
}
template<typename T, typename... Args, typename = std::enable_if_t<is_valid<T,Args...>()> >
auto foo(Args&&... args) {}
Consider the following code snippet:
template <bool> struct B { };
template <typename T>
constexpr bool pred(T t) { return true; }
template <typename T>
auto f(T t) -> decltype(B<pred(t)>{})
{
}
clang++ (trunk) compiles the code
g++ (trunk) fails compilation with the following error:
src:7:34: error: template argument 1 is invalid
auto f(T t) -> decltype(B<pred(t)>{})
^
src:7:34: error: template argument 1 is invalid
src:7:34: error: template argument 1 is invalid
src:7:34: error: template argument 1 is invalid
src:7:34: error: template argument 1 is invalid
src:7:34: error: template argument 1 is invalid
src:7:25: error: invalid template-id
auto f(T t) -> decltype(B<pred(t)>{})
^
src:7:36: error: class template argument deduction failed:
auto f(T t) -> decltype(B<pred(t)>{})
^
src:7:36: error: no matching function for call to 'B()'
src:1:24: note: candidate: 'template<bool <anonymous> > B()-> B<<anonymous> >'
template <bool> struct B { };
^
src:1:24: note: template argument deduction/substitution failed:
src:7:36: note: couldn't deduce template parameter '<anonymous>'
auto f(T t) -> decltype(B<pred(t)>{})
^
live example on godbolt.org
Even though g++'s diagnostic is misleading, I assume that the problem here is that t is not a constant expression. Changing the code to...
decltype(B<pred(T{})>{})
...fixes the compilation error on g++: live example on godbolt.org
What compiler is behaving correctly here?
GCC is wrong. There is no rule that prevents using a function's parameters in a constant expression in this way.
However, you cannot use the value of the parameter in such a context, and the set of types T for which f is callable is quite restricted. To see why, we need to consider what constructs will be evaluated when evaluating the expression pred(t):
// parameters renamed for clarity
template <typename U>
constexpr bool pred(U u) { return true; }
template <typename T>
auto f(T t) -> decltype(B<pred(t)>{});
The evaluation semantics for the call pred(t) are as follows:
copy-initialize pred's parameter u from f's parameter t
evaluate the body of pred, which trivially creates a bool value true
destroy u
So, f is only callable for types T for which the above only involves constructs that are valid during constant evaluation (see [expr.const]p2 for the rules). The requirements are:
T must be a literal type
copy-initialization of u from t must be a constant expression, and in particular, must not perform an lvalue-to-rvalue conversion on any member of t (because their values are not known), and must not name any reference member of t
In practice, this means that f is callable if T is an empty class type with a defaulted copy constructor, or if T is a class type whose copy constructor is constexpr and does not read any members of its argument, or (strangely) if T is std::nullptr_t (although clang currently gets the nullptr_t case wrong).
The compiler is expecting a parameter in that context because it needs to evaluate the full (template overloaded) function type. Given the implementation of pred, any value would work in that location. Here it is binding the f parameter's template type to the argument.
The g++ compiler appears to be making a simplifying assumption that a template constexpr function will somehow be altered by any parameters unless they are also const, which, as you've demonstrated, and clang agrees, is not necessarily the case.
It all comes down to how deep inside the function implementation the compiler goes to mark the function as non-const due to non-const contribution to the return value.
Then there is the question of whether the function is instantiated and requires the compiler to actually compile the code vs performing template parsing which, at least with g++, appears to be a different level of compilation.
Then I went to the standard and they kindly allow the compiler writer to make exactly that simplifying assumption and the template function instantiation should only work for f<const T> or f <const T&>
constexpr` functions must have: each of its parameters must be
LiteralType
So the template code should compile but fail if instantiated with a non-const T.
t is not constexpr value, this mean pred(t) is not constexpr too.
You can't use it in B<pred(t)> because this need constexpr.
This version compile correctly:
template <bool> struct B { };
template <typename T>
constexpr bool pred(T t) { return true; }
template <typename T, T t>
auto f() -> decltype(B<pred(t)>{})
{
}
https://godbolt.org/g/ydbj1X
Another valid code is:
template <typename T>
auto f(T t) -> decltype(pred(t))
{
}
This is because you do not evaluate pred(t) only you get type information.
B<pred(t)> need evaluation of pred(t) other wise you will get B<true> or B<false>, for any normal value you can't to this.
std::integral_constant<int, 0>{} can work in Clang case is probably because its value build in as part of type and always is same. If we change code a bit:
template <typename T>
auto f(T t) -> decltype(B<pred(decltype(t){})>{})
{
return {};
}
both Clang and GCC compile it, in case std::integral_constant, both t and decltype(t){} have always same value.
The following code compiles and runs ok.
void foo() {
}
template <typename T, typename... Args>
void foo(T x, Args... args) {
cout << x << endl;
foo(args...);
}
// inside main()
foo(1,1,1);
This other code does not compile:
void foo() {
}
template <typename... Args, typename T>
void foo(Args... args, T x) {
foo(args...);
cout << x << endl;
}
// inside main()
foo(1,1,1);
The compiler says that there is no matching function for call to foo(1,1,1) and says that foo(Args... args, T x) is a candidate, but template argument deduction/substitution failed, because candidate expects 1 argument, but 3 were provided.
Is there any ambiguity with this situation that no compiler can handle? This compile error just seems illogical to me. Maybe this is not in accordance, on purpose, with the C++ standard?
(This answer is based on #JohannesSchaub-litb's comments)
According to the standard, template parameter pack is not deducible if it is used in a function parameter pack not at the end of the parameter list.
§14.8.2.1/1 Deducing template arguments from a function call
[temp.deduct.call]:
When a function parameter pack appears in a non-deduced context
([temp.deduct.type]), the type of that parameter pack is never
deduced. [ Example:
template<class T1, class ... Types> void g1(Types ..., T1);
void h(int x, float& y) {
const int z = x;
g1(x, y, z); // error: Types is not deduced
g1<int, int, int>(x, y, z); // OK, no deduction occurs
}
— end example ]
And about non-deduced context, §14.8.2.5/5 Deducing template arguments from a type
[temp.deduct.type]:
A function parameter pack that does not occur at the end of the parameter-declaration-list.
So the direct reason of foo(1,1,1); failed is that the template parameter Args is not deduced, which is necessary to make the function invocation valid.
To explain the error message, a template parameter pack not deduced will be deduced to an empty sequence of template arguments[1], it means it'll be omitted. Then foo(1,1,1); failed because the number of arguments doesn't match, that's what compiler complained.
Just as the example from standard shown, you could specify the template argument explicitly to avoid type deduction, even though it doesn't meet the original intent of your code. Such as:
template <typename T, typename... Args>
void foo(Args... args, T x) {
}
int main() {
// inside main()
foo<int, int, int>(1, 1, 1);
}
Here're some additional informations.
[1] I can't find direct expression about this in the standard. The most close one is this, "A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced to an empty sequence of template arguments."
The interesting part from Clang's error message is:
main.cpp:11:6: note: candidate template ignored: couldn't infer template argument 'T'
void foo(Args... args, T x) {
^
The problem is that the parameter pack Args... occurs prior to T.
Args... is "greedy" and so no parameters are left for the compiler to deduce T, hence it fails.
Quoting the standard (emphasis mine):
[temp.param]/11
A template parameter pack of a function template shall not be followed
by another template parameter unless that template parameter can be
deduced from the parameter-type-list of the function template or has a
default argument. [Example:
...
// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { } // error
template<class... T, class U> void g() { } // error
— end example]
Goal : Obtain a callback function that will take any type of parameters as the callback function's parameters
.h
template <typename F, typename A>
void DelayedCallback(F&& CallbackFunction, A&& Args = NULL);
/
.cpp
void DelayedCallback(F&& CallbackFunction, A&& Args)
{
//Timer function that received a function pointer to be the "callback" function
Timer((std::bind(std::forward<F>(CallbackFunction), std::forward<A>(Args)))())
}
/
DelayedCallback(&HUDExit);
void HUDExit() {}
/
ERROR : DelayedCallback(FName,float,F &&,A &&)' : could not deduce template argument for 'A'
What am I doing wrong? I'm new to most of these concept in c++, more of c# programmer
EDIT : It's not only about the error, I'm pretty sure it's not the only one I am making.
Your error message doesn't match the signature of DelayedCallback
template <typename F, typename A>
void DelayedCallback(F&& CallbackFunction, A&& Args = NULL)
DelayedCallback(&HUDExit);
That function signature and the usage you've shown will not produce an error message that says
ERROR : DelayedCallback(FName,float,F &&,A &&)' : could not deduce template argument for 'A'
But ignoring the template parameter mismatches, the code you've shown will also result in a similar error. The problem is that template parameters cannot be deduced from default arguments and A is treated as a non-deduced context in your example.
From N3337, §14.8.2.5/5 [temp.deduct.type]
The non-deduced contexts are:
...
— 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.
Instead, you should change A to a parameter pack. That'll allow you to pass zero or more arguments to DelayedCallback.
template <typename F, typename... A>
void DelayedCallback(F&& CallbackFunction, A&&... Args)
{
//Timer function that received a function pointer to be the "callback" function
Timer((std::bind(std::forward<F>(CallbackFunction), std::forward<A>(Args)...))())
// previous line is missing a semicolon at the very least
}
Once you fix all that, you'll run into the problem mentioned in the comments. You cannot split the declaration and definition of a function template between a header and source file as you would with a non-template. So implement DelayedCallback in the header itself as I have done above.