When are template parameter packs deduced as empty? - c++

Consider the following examples (Coliru link):
template <class... T> struct S { using type = int; };
template <class... T>
void f(typename S<T...>::type) {
static_assert(sizeof...(T) == 0);
}
template <class... T>
void g(typename S<T...>::type, S<T...>*) {}
int main() {
f(42);
g(42, nullptr);
}
GCC and Clang are both happy with the call to f, but not the call to g.
In the call to f, although T... appears in a non-deduced context, it ends up being deduced as empty. This seems to be due to [temp.arg.explicit]/4:
... A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments. ...
In the call to g, however, the fact that T... additionally appears in a deduced context, which causes deduction to be attempted and failed, seems to cause g to become non-viable. There seems to be no "fallback" to T... being empty once deduction has been attempted and failed.
Is this behaviour intended? If so, why?
If so, was it intended that the "not otherwise deduced" wording specifies this behaviour? (i.e., it implies that the empty fallback only occurs if the pack appears in no deduced contexts)
If so, is this wording clear enough? It seems a plausible alternative reading of "not otherwise deduced" is "either deduction was not done, or deduction was attempted and failed".

... A trailing template parameter pack ([temp.variadic]) not otherwise
deduced will be deduced as an empty sequence of template arguments.
...
It could be enough to say that the not otherwise deduced is not a clause that should somehow or automagically relax some other rules or actually it is has nothing to do with why it's malformed (contrary to what I think you imply).
This other rule is perhaps best demonstrated by another very simple example:
template<class T>
void f(T, T){};
int main() {
f(int{42},short{42});
}
The above fails to compile. Why? Because even while short converts to int seamlessly (promotion), it is not the same type.
Additionally since nullptr just has the somewhat plain type of std::nullptr_t - it is very ill-suited to participate in template argument deduction at all.
So let's forget about non-deduced context for a moment, and try with deduced one:
template <class... T>
void g(S<T...>*, S<T...>* ) {}
int main() {
S<> s1;
g(&s1, nullptr);
}
or if you prefer, just
int main() {
S<> s1;
g(&s1, 0);
}
and both fail for the same reason.
Now, if you would like to allow conversion - then use an identity template - and this even works for the non-deduced context!
For your case, the example could look like (c++2a):
template <class... T>
void g(typename S<T...>::type, std::type_identity_t<S<T...> >*) {}
int main() {
f(42);
g(42, nullptr);
}
Which is valid. (note if you don't have have c++20 just write the identity template yourself)
As stated in a comment, turning the question around could perhaps lead to a more interesting question ?
What's the reasoning for allowing empty template argument deduction in non-deduced contexts?

Related

Template parameters of function type with auto return type arguments of previous template parameter types

I have a template with two parameters: the first is a type, and the second is a function pointer with an argument whose type is the first template parameter. This MCVE works:
void returnsVoid(int x) { }
template <typename T, void (*Func)(T)>
struct foo { void bar(T t) { Func(t); } };
int main(int, char *[]) {
foo<int, returnsVoid> a; // ok
}
However, when I change the return type of the second template parameter to auto (as explained in this related question), I get an error:
void returnsVoid(int x) { }
template <typename T, auto (*Func)(T)>
struct foo {
void bar(T t) { Func(t); }
};
int main(int, char *[]) {
foo<int, returnsVoid> a; // error: unable to deduce ‘auto (*)(T)’ from ‘returnsVoid’
// note: mismatched types ‘T’ and ‘int’
}
Why does this no longer work with an auto return type?
I'm using g++ 9.3.0 on an Ubuntu Linux machine. A similar error occurs in clang 10.
This is gcc's bug. (Bug 83417)
It seems gcc failing deducing the type of auto when using the 1st template parameter T as function parameter; you can use std::type_identity (since C++20) to exclude it from participating in template argument deduction.
// workaround for gcc
template <typename T, auto (*Func)(std::type_identity_t<T>)>
struct foo {
void bar(T t) { Func(t); }
};
LIVE
BTW: Clang 10 seems working well.
All standard references below refer, unless noted otherwise, to N4861 (March 2020 post-Prague working draft/C++20 DIS).
TL;DR;
Jump to the A workaround to mitigate the GCC bug section in the bottom of this post for a workaround, accepted by GCC and Clanng, which still relies of deduction of dependent types to avoid a client having to specify a second template argument for the actual type of the function parameter associated with the "main" template argument; namely the function pointer used associated non-type template parameter.
Standardese
As per [temp.deduct.type]/13:
When the value of the argument corresponding to a non-type template
parameter P that is declared with a dependent type is deduced from
an expression, the template parameters in the type of P are deduced
from the type of the value. [ Example:
template<long n> struct A { };
template<typename T> struct C;
template<typename T, T n> struct C<A<n>> {
using Q = T;
};
using R = long;
using R = C<A<2>>::Q; // OK; T was deduced as long from the
// template argument value in the type A<2>
— end example ]
any dependent types in the declaration of a non-type template parameter that undergoes type deduction (from an expression) shall also be deduced from the associated argument for the non-type template parameter. It is also essential that [temp.deduct.type]/5, covering the non-deduced contexts, does not apply for general uses of dependent types within non-type template parameters that are function pointers; meaning in the OP's example, T is a dependent type and is thus deduced from the value of the argument to the non-type (function pointer) template parameter.
A common problem when a given, say, type template parameter is deduced from more than one source (e.g. as in the example of OP), is that deduction yields different types; e.g. as showing in the following blog post:
template<typename T>
struct Foo { T t; };
template<typename T>
void addToFoo(Foo<T>& foo, T val) { foo.t += val; }
int main() {
Foo<long> f{42};
addToFoo(f, 13); // error: no matching function for call to 'addToFoo'
// note: candidate template ignored: deduced conflicting
// types for parameter T (long vs. int).
return 0;
}
As has been shown in #songyuanyao: answer (and as is shown also in the blog post), a type identity transformation trait can be used to intentionally place a given template parameter in a non-deduced context for cases where several deduction sources yields conflicting results.
However, the root cause of OP:s failure is not conflicting deduction results (this is a red herring), but rather GCC:s failure to correctly deduce template parameter from when another template parameter is deduced, where the former is present as a dependent type.
Thus, if we go back to [temp.deduct.type]/13, for the following class template and subsequent partial specialization:
// #1
template <auto>
struct A { static void dispatch() = delete; };
// #2
template <typename T, void (*fun)(T)>
struct A<fun> {
static void dispatch() {
T t{};
fun(t);
}
};
the following:
A<f>::dispatch(); // #3
is well-formed if f is a function with a single argument (of a type that is default-constructible) which returns void, e.g.
void f(int) { std::cout << "void f(int)\n"; }
// -> #3 is well-formed
as this will match the partial specialization at #2, deducing T to int and the non-type template parameter (which decides the specialization blueprinted by the primary template), which is dependent on T in this partial specialization, to void(*)(int).
On the other hand, #3 is ill-formed if f does not return void, as the partial specialization at #2 is no longer viable.
void f(int) { std::cout << "int f(int)\n"; }
// -> #3 is ill-formed
The key here is that:
the partial specialization at #2 applies only for template arguments to A which match the second (non-type) template parameter of the specialization, and
the first template parameter of the specialization,T, is deduced from the deduction of the second (non-type) template parameter, as T is a dependent type in the declaration of the second template parameter.
Both GCC and Clang works as expected for the two cases above.
Now, if we consider the similar example as to that of #1 and #2 above:
// #4
template <auto>
struct B { static void dispatch() = delete; };
// #5
template <typename T, auto (*fun)(T)>
struct B<fun> {
static void dispatch() {
T t{};
fun(t);
}
};
// ... elsewere
// #6
B<f>::dispatch();
the same argument as above applies:
if template argument f refers to a function (now with less restrictions) that has a single argument (of a type that is default-constructible), then the partial specialization at #5 is viable, and its second non-type template parameter, which contains its first type template parameter as a dependent type, shall be used to deduce the latter.
The significant difference in this case is that the type of the non-type template parameter itself undergoes [temp.arg.nontype]/1:
If the type T of a template-parameter contains a placeholder type
([dcl.spec.auto]) or a placeholder for a deduced class type
([dcl.type.class.deduct]), the type of the parameter is the type
deduced for the variable x in the invented declaration
T x = template-argument ;
If a deduced parameter type is not permitted for a template-parameter
declaration ([temp.param]), the program is ill-formed.
but [temp.deduct.type]/13 still applies the same, and we may not that [temp.deduct.type]/13 was actually added as part of P0127R2 (Declaring non-type template parameters with auto) which introduced placeholder types for non-type template parameters for C++17.
Thus, the core issue is that GCC fails to perform dependent type deduction (as specified in [temp.deduct.type]/13) when the non-type template parameter in whose declaration the dependent type (to be deduced) is present is a function pointer (or, as shown in the linked to GCC bug report, as pointer to member) AND the non-type template parameter is declared with a placeholder type (auto).
#songyuanyao: answer shows a workaround to this bug, applied to OP's example, simply making the dependent type non-dependent, as the associated template parameter can be deduced from elsewhere (namely from the first template argument in OP's example). This would not work for the examples above, where we rely solely on the dependent type deduction in the deduction of the non-type template parameter to find the type of the type template parameter (which is the dependent type in the former).
For an actual client API, requiring the client to explicitly specify the type of the argument to the function which is provided as another argument, when the former is entirely deducible from the latter, is arguably redundant design, and opens up for client confusion when providing conflicting arguments for these two template parameters. Thus, we'd arguably like to fall back on the partial specialization technique shown above, but as shown in these answers, GCC fails us in this regard in case we'd like the client to not be restricted to a specific return type.
A workaround to mitigate the GCC bug
We can work our way around this, however, by using the same approach as above, still relying on [temp.deduct.type]/13, but by using an additional type template parameter (for the return type) in the partial specialization:
#include <iostream>
template <auto>
struct C { static void dispatch() = delete; };
template <typename T, typename Return, Return (*fun)(T)>
struct C<fun> {
static void dispatch() {
T t{};
fun(t);
}
};
void f(int) { std::cout << "void f(int)\n"; }
int g(int) { std::cout << "int f(int)\n"; return 0; }
int main() {
C<f>::dispatch();
C<g>::dispatch();
}
The client will not need to worry about the additional template parameters of the partial specialization, as they entirely deducible via the third non-type template parameter of the specialization, in whose declaration they are dependent. This final example is accepted by both GCC and Clang.
This is CWG2476: [dcl.spec.auto]/2 requires that auto used as (part of) a return type without a trailing-return-type appear only where the function declarator declares a function. The template parameter declaration doesn’t do that, so it’s invalid. One could argue that /5 allows it in a template parameter’s decl-specifier-seq regardless, but that doesn’t make sense because it would deprive the following of meaning:
template<auto g() -> int>
int f() {return g();}
That said, the deduction rules would work here, so this is a defect in the wording that will probably be fixed by saying that return type deduction just doesn’t happen in the cases where auto is allowed for other reasons.
As already stated by #songyuanyao this seems to be a gcc bug.
For <= c++17 One solution is to move type T form function signature to non-deduced context:
template <typename T>
struct identity { using type = T; };
and then
template <typename T, auto (*Func)(typename identity<T>::type)>
struct foo {
void bar(T t) { Func(t); }
};
Another approach is to change design and to move to a member function template. The auto type deduction works in this case:
void returnsVoid(int) { }
template <typename T>
struct foo {
template <auto (*Func)(T)>
void bar(T t) { Func(t); }
};
int main(int, char *[]) {
foo<int> a;
a.bar<returnsVoid>(3);
}
Live

Strange inconsistency between function template and "normal" function

I have the two functions that are almost the same (with the exception that one of them is a template):
int* bar(const std::variant<int*, std::tuple<float, double>>& t)
{
return std::get<0>(t);
}
template <typename... Args>
int* foo(const std::variant<int*, std::tuple<Args...>>& t)
{
return std::get<0>(t);
}
Than, they are use like this:
foo(nullptr);
bar(nullptr);
The second one compiles and returns (int*)nullptr, but the first one doesn't (in Visual Studio 2019 using C++17 giving the error foo: no matching overload found). Why? Why does making this function a template cause it to cease to compile?
Using foo like below doesn't help either, so the inability to deduce Args is probably not the problem:
foo<>(nullptr);
In contrary, the following does work:
foo(std::variant<int*, std::tuple<>>(nullptr));
Is it possible to somehow avoid the need to write this in such a long manner?
Apparently if a type of a function parameter depends on a template parameter that has to be deduced (because it's not specified in <...>), then implicit conversions don't apply when passing an argument to that parameter.
Source:
The function parameters that do not participate in template argument
deduction (e.g. if the corresponding template arguments are explicitly
specified) are subject to implicit conversions to the type of the
corresponding function parameter (as in the usual overload
resolution).
A template parameter pack that is explicitly specified may be extended
by template argument deduction if there are additional arguments:
template<class ... Types> void f(Types ... values);
void g() {
f<int*, float*>(0, 0, 0); // Types = {int*, float*, int}
}
This also explains why foo<>(nullptr); still doesn't work. Since the compiler tries to deduce additional types to extend Args, in this case there doesn't seem to be any difference between foo(nullptr); and foo<>(nullptr);.
When a template function is considered it will only work for an exact match of the argument types at the call. This means that no conversions will be made (except for cv qualifiers).
A simple workaround in your case would be to make a function catch std::nullptr_t and forward that to your template.
int* foo(std::nullptr_t) {
return foo(std::variant<int*, std::tuple<>>{nullptr});
}
I would avoid this construct simply because the rules about exactly how the compiler will (if it even does it at all) resolve the overload are so confusing that I couldn't really tell you what it did without looking at a standards document, and code like that should be avoided. I would force the overload resolution you want this way instead:
template <typename... Args>
int *foo(const ::std::variant<int*, ::std::tuple<Args...>> &t)
{
return ::std::get<0>(t);
}
int *foo(int *ip)
{
using my_variant = ::std::variant<int *, ::std::tuple<>>;
return foo(my_variant{ip});
}
template <typename... Args>
int *foo(::std::tuple<Args...> const &t)
{
using my_variant = ::std::variant<int *, ::std::tuple<Args...>>;
return foo(my_variant{t});
}
template <typename... Args>
int *foo(::std::tuple<Args...> &&t)
{
using my_variant = ::std::variant<int *, ::std::tuple<Args...>>;
return foo(my_variant{::std::move(t)});
}

Why are variadic templates different than non-variadic, for only one argument?

This code compiles just fine:
template <typename T1>
struct Struct {
};
struct ConvertsToStruct {
operator Struct<int>() const;
};
template <typename T>
void NonVariadicFunc(Struct<T>);
int main() {
NonVariadicFunc<int>(ConvertsToStruct{});
return 0;
}
But an attempt to make it a little more generic, by using variadic templates, fails to compile:
template <typename T1>
struct Struct {
};
struct ConvertsToStruct {
operator Struct<int>() const;
};
template <typename... T>
void VariadicFunc(Struct<T...>);
int main() {
VariadicFunc<int>(ConvertsToStruct{});
return 0;
}
What's going wrong? Why isn't my attempt to explicitly specify VariadicFunc's template type succeeding?
Godbolt link => https://godbolt.org/g/kq9d7L
There are 2 reasons to explain why this code can't compile.
The first is, the template parameter of a template function can be partially specified:
template<class U, class V> void foo(V v) {}
int main() {
foo<double>(12);
}
This code works, because you specify the first template parameter U and let the compiler determine the second parameter. For the same reason, your VariadicFunc<int>(ConvertsToStruct{}); also requires template argument deduction. Here is a similar example, it compiles:
template<class... U> void bar(U... u) {}
int main() {
bar<int>(12.0, 13.4f);
}
Now we know compiler needs to do deduction for your code, then comes the second part: compiler processes different stages in a fixed order:
cppreference
Template argument deduction takes place after the function template name lookup (which may involve argument-dependent lookup) and before template argument substitution (which may involve SFINAE) and overload resolution.
Implicit conversion takes place at overload resolution, after template argument deduction. Thus in your case, the existence of a user-defined conversion operator has no effect when compiler is doing template argument deduction. Obviously ConvertsToStruct itself cannot match anything, thus deduction failed and the code can't compile.
The problem is that with
VariadicFunc<int>(ConvertsToStruct{});
you fix only the first template parameter in the list T....
And the compiler doesn't know how to deduce the remaining.
Even weirder, I can take the address of the function, and then it works
It's because with (&VariadicFunc<int>) you ask for the pointer of the function (without asking the compiler to deduce the types from the argument) so the <int> part fix all template parameters.
When you pass the ConvertToStruct{} part
(&VariadicFunc<int>)(ConvertToStruct{});
the compiler know that T... is int and look if can obtain a Struct<int> from a ConvertToStruct and find the apposite conversion operator.

Template arguments deduction for parameter type of function pointer involving non-deduced parameter pack

This is similar to the question, but a more specific case. This time, no compiler work as expected.
template<class T>
struct nondeduced
{
using type = T;
};
template<class T>
using nondeduced_t = typename nondeduced<T>::type;
template<class... T, class U>
void f(void(*)(nondeduced_t<T>..., U)) {}
void g(int, char) { }
int main()
{
f<int>(g); // error?
}
In the above example, the parameter pack T cannot be deduced, but the compiler should be able to deduce U after explicit arguments substitution for pack T (i.e. single int in this case).
The above is expected to work without the nondeduced_t trick as well:
template<class... T, class U>
void f(void(*)(T..., U)) {}
Because the parameter pack T is already in non-deduced context according to
[temp.deduct.type]p5
The non-deduced contexts are:
A function parameter pack that does not occur at the end of the parameter-declaration-list.
Unfortunately, no compiler I tested (g++/clang) accept the code.
Notably something like below works on both g++ & clang.
template<class... T>
void f(void(*)(nondeduced_t<T>..., char)) {}
And again, this doesn't work on both:
template<class... T>
void f(void(*)(T..., char)) {}
Is my expectation wrong?
By [temp.deduct.type]p5 one of the non-deduced-context is
A function parameter pack that does not occur at the end of the parameter-declaration-list.
Parameter packs that doesn't appear as the last argument of a template functions are never deduced, but is completely right to specify the parameter types disabling the deduction. e.g
template<class T1, class ... Types> void g1(Types ..., T1);
g1<int, int, int>(1,2,3); // works by non-deduction
g1(1,2,3) // violate the rule above by non-deduced context
But changing the order of function argument even leaving the template parameters as they are, remove the non-deduced context condition and break the infinite expansion of parameter pack. e.g
template<class T1, class ... Types> void g1(T1, Types ...);
g1(1,2,3) // works because its a deduced context.
There're two reasons your code don't compile:
The order of function argument create a non-deduced-context which cause the type of the parameter pack T in the pattern stated in function f would never be deduced.
The template parameter T appears only as a qualifiers in function arguments(e.g nondeduced_t) and not directly specified as a function argument(which allow argument deduction).
To make the code compile you have either place the expansion of the parameter pack as it is forgetting the nondeduced_t indirect, as
template<class... T,class U>
void f( void(*)(U,T...) ) { }
f(g);
or changing the order of template parameters and specify the template argument on function call, as
template<class U,class... T>
void f( void(*)(U,typename nondeduced<T>::type...) ) {}
f<int,char>(g);

Make a code "forwarding referencable"

I opened this post about forwarding reference, this is a (hopefully) MCVE code:
#include <functional>
#include <vector>
using namespace std;
struct MultiMemoizator {
template <typename ReturnType, typename... Args>
ReturnType callFunction(std::function<ReturnType(Args...)> memFunc, Args&&... args) {
}
};
typedef vector<double> vecD;
vecD sort_vec (const vecD& vec) {
return vec;
}
int main()
{
vecD vec;
std::function<vecD(const vecD&)> sortFunc(sort_vec);
MultiMemoizator mem;
mem.callFunction<vecD, vecD>(sortFunc, vec);
}
Since this is not the whole code, maybe I'll have to add extra code based on the answers.
Anyway, as was suggested in this answer, forwarding reference is not possible with this version, since Args is not deduced.
So my question is: is it possible to make this code "forwarding referencable"?
In order to perfect-forward your arguments, you need to have the types deduced. You can do this by deducing the arguments to the function and the parameters to the functor separately:
template <typename ReturnType, typename... FunArgs, typename... Args>
ReturnType callFunction(std::function<ReturnType(FunArgs...)> memFunc,
Args&&... args)
{
//...
}
Then you can call callFunction without template parameters and have everything deduced:
mem.callFunction(sortFunc, vec);
I will add a bit of details regarding #TartanLlama answer on why your code fails to compile (even without the explicit template parameters) but also why (in my own opinion) your code is dangerous.
In the following, I will use only a simple type T instead of your parameter pack Args... because it is simpler to explain and does not change the meaning.
A bit of reminder on forwarding references...
First, let's take a simpler example than yours with simply the following:
template <typename T>
void f (T&&);
Now, let's instanciate f from various sources, let's assume with have the following variables:
std::string s;
const std::string cs;
...then:
f(s); // instanciate f<std::string&>
f(cs); // instanciate f<const std::string&>
f(std::string()); // instanciate f<std::string&&>
You should be wondering: Why is the first instanciation f<std::string&> instead of f<std::string>?, but the standard tells you (§14.8.2.1#3 [temp.deduct.call]):
If P is a forwarding reference and the argument is an
lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
Back to our initial snippet!
Now, let's complicate a bit our example:
template <typename T>
struct A {};
template <typename T>
void f (A<T>, T&&);
And one instantiation:
std::string s;
A<std::string> as;
f(as, s);
The above is equivalent to your example, and will fails to compile, but why... ? Well, as explained above, when you have an lvalue, the deduced type for T&& is T&, not T, and thus the type deduction fails for A<T> because the compiler is expecting A<std::string&> and you are giving a A<std::string>.
So now we know that we have to do the following:
A<std::string&> ars;
A<std::string const&> acrs;
f(ars, s); // good
f(acrs, cs); // good
Why is it dangerous?
Ok so now, this should be good:
A<std::string&&> arrs;
f(arrs, std::string());
But it is not... Because when T is deduced as a rvalue reference, T is simply T, so the compiler is expecting A<std::string>.
So here is the problem: You are going to give a rvalue to a method that is going to forward it to a function expecting an lvalue. That's not wrong, but it is probably not what you'd have expected.
How to deal with it?
The first possibility is to force the type of the first parameter regardless of the deduced type for T, e.g.:
template <typename T>
void f (A<typename std::remove_reference<T>::type>, T&&);
But note:
You would have to add more stuff to deal with const.
One may wonder the usefulness of T&& when the type of the first argument is fixed (in your case, at least).
The second possibility (warning: I don't know if this is standard!) is to move the first parameter at the end and then deduce the type from t:
template <typename T>
void f (T &&t, A<decltype(std::forward<T>(t))>);
Now you have an exact match between the deduced type for T and the expected type for A.
Unfortunately I don't know how to make the above work with variadic templates...