This is a point about which gcc 4.9.2 and clang 3.5.2 are in sharp
disagreement. The program:
template<typename ...Ts>
int foo(int i = 0, Ts &&... args)
{
return i + sizeof...(Ts);
}
int main()
{
return foo();
}
compiles without comment from gcc (-std=c++11 -Wall -pedantic). Clang says:
error: missing default argument on parameter 'args'
With foo amended to:
template<typename ...Ts>
int foo(int i = 0, Ts &&... args = 0)
{
return i + sizeof...(Ts);
}
clang has no complaints, but gcc says:
error: parameter pack ‘args’ cannot have a default argument
Which compiler is right?
From 8.3.6 ([dcl.fct.default])/3:
A default argument shall not be specified for a parameter pack.
From 8.3.6 ([dcl.fct.default])/4:
In a given function declaration, each parameter subsequent to a parameter with a default argument shall have a default argument supplied in this or a previous declaration or shall be a function parameter pack.
So this allows code like void f(int a = 10, Args ... args), or indeed like your first snippet. (Thanks to #T.C. for looking up the second sentence!)
A Kerrek SB says, it's not possible. What you could do, instead, is using a std::tuple
template <class ... Args>
void foo( std::tuple<Args...> t = std::tuple<int>(0) )
{}
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) {}
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]
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.
It seems that GCC treats expanding an empty parameter pack A into another
parameter pack B differently than manually entering an empty parameter pack
B. Example:
void baz();
void baz(int);
template<typename... Args, typename R>
void bar(R (*)(Args...));
template<typename... Args>
void foo()
{
bar<Args...>(baz);
}
int main()
{
foo<>(); // Deduces baz()
//bar<>(baz); // Ambiguous
foo<int>(); // Deduces baz(int)
bar<int>(baz); // Deduces baz(int)
//foo<void>(); // Ambiguous
//bar<void>(baz); // Ambiguous
}
Is this behaviour standard conforming? You can see a live example of the difference here and here.
f<> doesn't force empty pack, just tells the first 0 arguments of the template argument.
Clang rejects foo<>() (Demo) as it can't decide which overload of baz for bar<>(baz);
The fact that gcc accepts it seems to be a bug.