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.
Related
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) )
{}
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
}
Given a non-variadic function template:
template<class T>
void f(void(t)(T));
And some plain functions:
void f1(int);
void f2(char);
This works:
f(f1);
The type of t becomes void (*)(int).
However, the variadic counterpart:
template<class... T>
void f(void(...t)(T));
// call
f(f1, f2);
does not work. The compilers (gcc & clang) complain about mismatched types void(T) and void (*)(int). See DEMO.
Note that if * is added explicitly, it works as it should:
template<class... T>
void f(void(*...t)(T));
So, why the non-variadic one can decay the function type while the variadic one cannot?
AFAICS, the code is fine (also supported by the fact that both VC++ and ICC compile it). After all, template argument deduction seems to work with function types just as it does with function pointer or reference types; [temp.deduct.type]/18:
A template-argument can be deduced from a function […] type.
[temp.deduct.call]/1:
For a function parameter pack that occurs at the end of the parameter-declaration-list,
deduction is performed for each remaining argument of the call, taking the type P of the declarator-id of the
function parameter pack as the corresponding function template parameter type. Each deduction deduces
template arguments for subsequent positions in the template parameter packs expanded by the function
parameter pack.
In particular the latter paragraph confirms that there is some inconcistency, since the (unsuccessful) deduction of the pack T in the second case reduces to the (successful) deduction in case 1.
My guess is that Clang and GCC decay the parameter types for function templates right at declaration time, but refuse to do so when the parameter is a pack expansion (and then fail to deduce). Clang's error message when we alter the sample call to f(f1) is
note: candidate template ignored: could not match 'void (T)' against 'void (*)(int)'
So the argument is in fact decayed before deduction.
Because your syntax is not quite right.
template<class... T>
void f(void(t)(T...));
void f1(int);
void foo()
{
f(f1);
}
Tested with gcc 6.1.1. Compiles without errors.
In the non-variadic version, you are using the template parameter as the parameter to the arguments to the passed function parameter.
Therefore, for the variadic version, that's where the parameter pack gets expanded.
Does this explains?
template<class... T>
void f(void(*...t)(T)) {
std::cout << "Ptr to functions" << std::endl;
}
template<class... T>
void g(void(&...t)(T)) {
std::cout << "Ref to functions" << std::endl;
}
template <class... T>
void h(void(...t)(T)) {
std::cout << "Mmmm... potential of confusion" << std::endl;
}
void s1(int) {
}
void s2(long) {
}
#include <type_traits>
int main() {
// this compiles Ok. The compiler knows
// pointer to functions will be passed
// and can infer their correspondent T
f(s1,s2);
// this compiles Ok. The compiler knows that
// reference to functions will be passed
// and can infer their correspondent T
g(s1,s2);
h(s1,s2);
// ^^^^^^^^^ this will cause a compilation error telling:
// template argument deduction/substitution failed:
// mismatched types ‘void(T)’ and ‘void (*)(int)’
// The compiler can't decide how the function-type parameters are passed
// by ptr, by ref, one by ref other by ptr? As such
// cannot deduce their correspondent T in the argpack
// however, this will compile OK!!!
// The s1/s2 correspondent T-es in argpack are clearly specified,
// not a problem.
h<int,long>(s1,s2);
return 0;
}
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]
Consider the following code:
#include <iostream>
#include <type_traits>
// Variadic version
template<class... Variadic>
void f(const Variadic&... variadic)
{
std::cout<<"variadic"<<std::endl;
}
// Single version
template<class Single, class = typename std::enable_if<std::is_fundamental<Single>::value>::type>
void f(const Single& single)
{
std::cout<<"single"<<std::endl;
}
// Main
int main()
{
f(); // variadic
f(42); // single : why?
f(std::string()); // variadic
f(42, 42); // variadic
return 0;
}
I do not understand why the line marked "single" compiles well (under g++ 4.6.3) and does not produce an overload resolution problem. Does the c++11 standard say that a template function with a fixed number of parameters is prefered over a variadic function that could have the same signature ?
It is really quite simple (two live examples, gcc and clang)
template<class...T> void foo(T&&...) {std::cout << "...T\n";}
template<class T> void foo(T&&) {std::cout << "T\n";}
int main() {
foo(3);
}
Overloads not taking ... seem to be preferred when the choice is an explicit template parameter.
The class=std::enable_if_t does not change this.
So both your functions f are candidates, then the compiler prefers the one without variardics.
14.8.2.4 Deducing template arguments during partial ordering [temp.deduct.partial]
/8:
If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails. Otherwise, using the resulting types P and A, the deduction is then done as described in 14.8.2.5. If P is a function parameter pack, the type A of each remaining parameter type of the argument template is compared with the type P of the declarator-id
of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template. [
Example:
template<class... Args> void f(Args... args); // #1
template<class T1, class... Args> void f(T1 a1, Args... args); // #2
template<class T1, class T2> void f(T1 a1, T2 a2); // #3
f(); // calls #1
f(1, 2, 3); // calls #2
f(1, 2); // calls #3; non-variadic template #3 is more
// specialized than the variadic templates #1 and #
In particular, the f(1,2) example.
All the enable_if_t clause does is remove the one-argument version from consideration when you pass a std::string as T.
Due to the use of the second, enable_if, template parameter in the 'single' version, the compiler considers that version to be a more-specialized template for use with the types for which it is enabled.
It is considered more specialized because there are types where the variadic template can be instantiated, but the 'single' can't.
The general rule is that a more specialized template trumps a less specialized template in overload resolution.