Using alias templates for sfinae: does the language allow it? - c++

I have just discovered the following technique. It looks very close to one of proposed concepts syntax, works perfectly on Clang, GCC and MSVC.
template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type>
using require_rvalue = T&&;
template <typename T>
void foo(require_rvalue<T> val);
I tried to find it with search requests like "sfinae in type alias" and got nothing. Is there a name for this technique and does the language actually allows it?
The full example:
#include <type_traits>
template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type>
using require_rvalue = T&&;
template <typename T>
void foo(require_rvalue<T>)
{
}
int main()
{
int i = 0;
const int ic = 0;
foo(i); // fail to compile, as desired
foo(ic); // fail to compile, as desired
foo(std::move(i)); // ok
foo(123); // ok
}

[...] does the language actually allows it?
Can't say anything about the name, but this seems to me to be a yes.
The relevant wording is [temp.alias]/2:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.
and the sfinae rule, [temp.deduct]/8:
Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.
Taking an argument of type require_rvalue<T> does behave as if we substitute that alias, which either gives us a T&& or a substitution failure - and that substitution failure is arguably in the immediate context† of the substitution and so is "sfinae-friendly" as opposed to being a hard error. Note that even though the defaulted type argument is unused, as a result of CWG 1558 (the void_t rule), we got the addition of [temp.alias]/3:
However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id.
This ensures that we still substitute into the defaulted type argument to trigger the required substitution failure.
The second unsaid part of the question is whether this actually can behave as a forwarding reference. The rule there is in [temp.deduct.call]/3:
A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])). 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.
Is an alias template with one template parameter whose associated type is an rvalue reference to its cv-unqualified template parameter considered a forwarding reference? Well, [temp.alias]/2 says that require_rvalue<T> is equivalent to T&&, and T&& is the right thing. So arguably... yeah.
And all the compilers treat it as such, which is certainly a nice validation to have.
†Although, note the existence of CWG 1844 and the lack of actual definition for immediate context, and the example there which also relies upon a substitution failure from a defaulted argument - which the issue states has implementation divergence.

It works and allowed because it relays on widely used C++ features allowed by the standard:
SFINAE in function parameters ([temp.over]/1, [temp.deduct]/6, [temp.deduct]/8):
template <typename T>
void foo(T&& v, typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = nullptr)
{ /* ... */ }
we cannot deduce on the actual parameter like void foo(typename std::enable_if<std::is_rvalue_reference<T&&>::value, T>::type&&) (CWG#549), but it is possible to workaround this limitation with template aliases (it is the trick I have presented in my question)
SFINAE in template parameter declaration ([temp.deduct]/7):
template <typename T, typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = nullptr>
void foo(T&& v)
{ /* ... */ }
Alias templates in function parameters ([temp.alias]/2):
template<class T> struct Alloc { /* ... */ };
template<class T> using Vec = vector<T, Alloc<T>>;
template<class T>
void process(Vec<T>& v)
{ /* ... */ }
Alias templates can have default parameters ([temp.param]/12, [temp.param]/15, [temp.param]/18)
Template parameters of alias templates parameterized with deducible types still can be deduced ([temp.deduct.type]/17):
I have accepted #Barry's answer and put this one (with concentrated info and about every aspect the trick uses) because a lot of people (including me) are scared of C++ standard voodoo language about template deduction stuff.

Related

Partial specialization of templates over non-type literal parameters in C++20: clang and gcc disagree

Toying around with literal, non-type template parameters in c++20, I found out that g++ and clang++ disagree about the following code.
#include <algorithm>
template<size_t N>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) {
std::copy_n(str, N, value);
}
char value[N];
};
template <typename T, StringLiteral Name>
struct named{};
template <typename T>
struct is_named: std::false_type{};
template <typename T, size_t N, StringLiteral<N> Name>
struct is_named<named<T, Name>>: std::true_type{};
// This will fail with g++
static_assert(is_named<named<int, "ciao">>::value == true);
See it live on godbolt: https://godbolt.org/z/f3afjd
First and foremost, I'm not even sure I'm doing it the right way: is it that the way one matches with a generic StringLiteral<N> type, or is it not? If not, what's the right way?
And, why do the compilers disagree about it? Who's got the bug?
EDIT: found out that removing the size_t N parameter in the partial specialization makes both compilers agree, and the result is the expected one. Like this:
template <typename T, StringLiteral Name>
struct is_named<named<T, Name>>: std::true_type{};
However, I'm still curious about whether my 1st attempt is legit, by the standard, and which compiler got it wrong.
Let's focus on the partial specialization of is_named:
template <typename T, size_t N, StringLiteral<N> Name>
struct is_named<named<T, Name>>: std::true_type{};
and particularly try to answer whether it violates [temp.class.spec.match]/3 or not:
If the template arguments of a partial specialization cannot be deduced because of the structure of its template-parameter-list and the template-id, the program is ill-formed.
noting that Clang apparently does not think so, and uses the single template argument to the primary template to deduce all template arguments of the partial specialization. In this particular case these template arguments are those matching its template-parameter-list:
the template argument for the type template parameter T
the template argument for the non-type template parameter N
the template argument for the non-type template parameter Name
As per [temp.deduct.type]/4:
[...] If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails. [...]
we can break the question down into whether all three template parameters T, N and Name of the partial specialization are used in least one deduced context or not.
Starting from [temp.deduct.type]/1
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
and [temp.deduct.type]/3 and (again) /4:
/3 A given type P can be composed from a number of other types, templates, and non-type values:
[...]
A type that is a specialization of a class template (e.g., A<int>) includes the types, templates, and non-type values referenced by the template argument list of the specialization. [...]
/4 In most cases, the types, templates, and non-type values that are used to compose P participate in template argument deduction.
We can without loss of generality consider the actual type of the template argument for the single type template parameter of the primary template (which we intend to be part fo the family of types for which partial specialization to applies), say A, as named<int, StringLiteral<5>{"ciao"}>.
The type specified in terms of the template parameters of the partial specialization, say P, is named<T, Name>.
T can be trivially deduced, matching A/P as named<int, StringLiteral<5>{"ciao"}>/named<T, Name>, to int, as T in named<T, Name> is not in a non-deduced context.
Name is similarly not in a non-deduced context, and can be deduced to StringLiteral<5>{"ciao"}.
The tricky part is N, which is not explicitly part of P, but only implicitly so via the template parameter Name. However, here we may simply apply the deduction rules recursively: Name has been deduced to StringLiteral<5>{"ciao"}, meaning we consider a new A/P pair StringLiteral<5>/StringLiteral<N>, in which N is non in a non-deducible context, and N can thus ultimately be deduced to 5.
And, why do the compilers disagree about it? Who's got the bug?
Consequently, Clang (as well as MSVC) are correct to accept your original variant, whereas GCC is wrong to reject it (rejects-valid bug).
A more minimal example which is (correctly) accepted by Clang and MSVC, and (incorrectly) rejected by GCC is:
template<int N> struct S {};
template<S s> struct U {};
template<typename> struct V { V() = delete; };
template <int N, S<N> s>
struct V<U<s>> {};
V<U<S<0>{}>> v{};
// Expected: use partial specialization #1
// GCC actual: error (rejects-valid): use of deleted function
and I have filed a bug report using this example:
Bug 99699 - Type deduction failure for deducing a non-type template parameter via another deducible structural type (class template specialization) non-type template parameter
[...] removing the size_t N parameter in the partial specialization makes both compilers agree, [...]
In your second variant, the deduction case is not as complex as the first one, and you can use a similar analysis to see that is likewise well-formed (all template parameters of the partial specialization are deducible).

C++14 lambda's default argument type deduction depending on preceding arguments

Is this not valid as C++14?
auto f = [](auto x, auto y = std::decay_t<decltype(x)>{}) { };
f(0);
I was expecting it to be roughly equivalent to
auto f = [](int x, int y) { };
f(0, int{});
Neither GCC 6.3 nor Clang 4.0 accepted my code.
http://ideone.com/b7b4SK GCC
http://ideone.com/EyLYaL Clang
Is it related to my lack of understanding of C++ template deduction phases? Does the 1400 pages long spec actually has an explicit answer to my question?
Update
To summarize, my problem can in fact be reduced to this piece of code (free of lambda, single parameter) and it is invalid under C++14 (thanks #BaummitAugen and #NirFriedman)
template <typename T>
void f(T x = 0) { }
int main() {
f();
}
The compilers are correct to reject your code, it is indeed not valid C++14.
In the standard (using N4141 here) we have
For a generic lambda, the closure type has a public inline function call
operator member template (14.5.2) whose template-parameter-list consists of one invented type template-
parameter for each occurrence of auto in the lambda’s parameter-declaration-clause, in order of appearance.
(5.1.2/4 [expr.prim.lambda]). So your call is equivalent to a call to some
template <class T1, class T2>
auto operator() (T1 x, T2 y = std::decay_t<decltype(x)>{});
Now
If a template parameter is used only in non-deduced
contexts and is not explicitly specified, template argument deduction fails.
(14.8.2/4 [temp.deduct.type]) and
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.
(14.8.2/5 [temp.deduct.type]) makes your call ill-formed.
I can't quote the spec, but I will quote cppreference which is an authoritative source and is often easier to read/follow. In particular, see http://en.cppreference.com/w/cpp/language/template_argument_deduction.
Non-deduced contexts
In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction...
You probably are already aware that template parameters cannot always be deduced. Going down the list of entries, we see:
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:
Which gives the following example:
template<typename T, typename F>
void f(const std::vector<T>& v, const F& comp = std::less<T>());
std::vector<std::string> v(3);
f(v);
variadic lambdas are basically equivalent to function templates with a type template parameter substituted for each usage of auto, so this example (which does not compile) is equivalent to your example.
So basically, the second type cannot be deduced because it is a non-deduced context.
This answer could probably be improved by giving a very good example of why exactly it's been decided to make this a non-deduced context, since naively it seems like it's possible. My guess is that this is basically because a function template is just that, a template for creating function. Defaulted arguments in turn, essentially create multiple callable signatures for the same function. So you can't really deal with defaulting, until you have a function, but you can't have a function until you instantiate, which requires knowing the template parameters.
It's instructive to note that this simpler example has the same issues:
template<typename T, typename F>
void f(const std::vector<T>& v, const F& comp = int{});
So the dependence on the first template parameter actually has nothing to do with the issue.

Can a forwarding reference be aliased with an alias template?

This is a continuation of my previous question:
Can an identity alias template be a forwarding reference?
It seems that the following code works in both Clang 3.7.0 (demo) and GCC 6.0.0 (demo):
template <class T>
using forwarding_reference = T&&;
template <class T>
void foo(forwarding_reference<T>) {}
int main()
{
int i{};
foo(i);
foo(1);
}
Are the compilers right to substitute the alias template for a forwarding reference and this could be a fancy way of writing one?
This is indeed standard compliant. §14.5.7/2:
When a template-id refers to the specialization of an alias template,
it is equivalent to the associated type obtained by substitution of
its template-arguments for the template-parameters in the type-id of
the alias template.
Now, consider that during template argument deduction, only the type of the parameter (in terms of template parameters) is inspected - §14.8.2.1/1:
Template argument deduction is done by comparing each function
template parameter type (call it P) with the type of the
corresponding argument of the call (call it A) as described below.
According to the first quote, the type of the parameter, i.e. forwarding_reference<T>, is equivalent to T&&. Hence P is T&& and there can be no difference regarding deduction.
This same conclusion was made by the committee in a defect report concerning this exact scenario, #1700:
Because the types of the function parameters are the same, regardless
of whether written directly or via an alias template, deduction must
be handled the same way in both cases.

Type deduction for non-viable function templates

In his answer to this question and the comment section, Johannes Schaub says there's a "match error" when trying to do template type deduction for a function template that requires more arguments than have been passed:
template<class T>
void foo(T, int);
foo(42); // the template specialization foo<int>(int, int) is not viable
In the context of the other question, what's relevant is whether or not type deduction for the function template succeeds (and substitution takes place):
template<class T>
struct has_no_nested_type {};
// I think you need some specialization for which the following class template
// `non_immediate_context` can be instantiated, otherwise the program is
// ill-formed, NDR
template<>
struct has_no_nested_type<double>
{ using type = double; };
// make the error appear NOT in the immediate context
template<class T>
struct non_immediate_context
{
using type = typename has_no_nested_type<T>::type;
};
template<class T>
typename non_immediate_context<T>::type
foo(T, int) { return {}; }
template<class T>
bool foo(T) { return {}; }
int main()
{
foo(42); // well-formed? clang++3.5 and g++4.8.2 accept it
foo<int>(42); // well-formed? clang++3.5 accepts it, but not g++4.8.2
}
When instantiating the first function template foo for T == int, the substitution produces an invalid type not in the immediate context of foo. This leads to a hard error (this is what the related question is about.)
However, when letting foo deduce its template-argument, g++ and clang++ agree that no instantiation takes place. As Johannes Schaub explains, this is because there is a "match error".
Question: What is a "match error", and where and how is it specified in the Standard?
Altenative question: Why is there a difference between foo(42) and foo<int>(42) for g++?
What I've found / tried so far:
[over.match.funcs]/7 and [temp.over] seem to describe the overload resolution specifics for function templates. The latter seem to mandate the substitution of template parameters for foo.
Interestingly, [over.match.funcs]/7 triggers the process described in [temp.over] before checking for viability of the function template (specialization).
Similarly, type deduction does not to take into account, say, default function arguments (other than making them a non-deduced context). It seems not to be concerned with viability, as far as I can tell.
Another possibly important aspect is how type deduction is specified. It acts on single function parameters, but I don't see where the distinction is made between parameter types that contain / are dependent on template parameters (like T const&) and those which aren't (like int).
Yet, g++ makes a difference between explicitly specifying the template parameter (hard error) and letting them be deduced (deduction failure / SFINAE). Why?
What I've summarized is the process described at 14.8.2.1p1
Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below.
In our case, we have for P (T, int) and for A, we have (int). For the first pair of P/A, which is T against int, we can match T to int (by the process described in 14.8.2.5). But for the second "pair", we have int but have no counterpart. Thus deduction cannot be made for this "pair".
Thereby, by 14.8.2.5p2, "If type deduction cannot be done for any P/A pair, ..., template
argument deduction fails.".
You then won't ever come to the point where you substitute template arguments into the function template.
This can all probably described more precisely in the Standard (IMO), but I believe this is how one could implement things to match the actual behavior of Clang and GCC and it seems a reasonable interpretation of the Standardese.

Type inference for `T::some_typredef` in Templated Function

Given a template function, that does not use the template parameter for input directly. How would C++ type inference work? For instance given
template<typename T>
void f(T::value_type){}
when (if at all) will type inference work for this function?
Is there any other place except of template<typename T1,...>void f(T1,T2,...) where type inference might occur?
As always, quote the standard for extra credit.
It never works for a nested type, unless T is also a parameter.
If you call f(1), the compiler has no chance of finding all T's with a nested typedef int value_type;.
You can infer a type that is part of the parameter type, like
template<class T>
void f(std::vector<T>);
I think here's your answer:
14.8.2.4 - Deducing template arguments from a type [temp.deduct.type]
[...]
-3- [...]
In most cases, the types, templates, and non-type values that are used to compose P participate in template argument deduction. That is, they may be used to determine the value of a template argument, and the value so determined must be consistent with the values determined elsewhere. In certain contexts, however, the value does not participate in type deduction, but instead uses the values of template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in nondeduced contexts and is not explicitly specified, template argument deduction fails.
-4- The nondeduced contexts are:
The nested-name-specifier of a type that was specified using a qualified-id.
A type that is a template-id in which one or more of the template-arguments is an expression that references a template-parameter.
When a type name is specified in a way that includes a nondeduced context, all of the types that comprise that type name are also nondeduced. However, a compound type can include both deduced and nondeduced types. [Example: If a type is specified as A<T>::B<T2>, both T and T2 are nondeduced. Likewise, if a type is specified as A<I+J>::X<T>, I, J, and T are nondeduced. If a type is specified as void f(A<T>::B, A<T>), the T in A<T>::B is nondeduced but the T in A<T> is deduced. ]
Your T::value_type is a qualified-id of a type, so types in its nested-name-specifier are nondeduced and must be specified explicitly.
Edit: this information is from ISO/IEC 14882:1998.
The standard requires you to disambiguate the dependent type:
template<typename T>
void f(typename T::value_type){}
In the past there have been some less-standard behaviours in this area, resulting in code that compiled on one (MSVC) but not on the other (GCC) compiler. These days, likely under the influence of heavily generic standard libraries like Boost, compilers seem to accept the correct code only.
The typename keyword is sometimes needed to disambiguate identifiers dependent on template arguments (also this). Think of it this way: you have to give the compiler enough information to complete a syntax check the first time 'round while parsing a template definition. The actual template arguments aren't known at that time and (C++ having an involved grammar) you have to give the compiler hints what kind of symbol a token is going to represent later on
Unless you specify the T parameter somehow, the compiler will not try to deduct T in any way.
Even if you specify it explicitly, I believe it will work only where the original definition is provided, not though typedefs.
Consider the following example (compiled with clang++, apparently g++ fails on that):
#include <stdio.h>
template <typename T>
void foo(T) {
printf("foo(T)\n");
}
template <typename T>
void foo(typename T::value) {
printf("foo(T::value)\n");
}
struct X {
class value {};
};
struct Z {
typedef int value;
};
struct XZ {
typedef Z value;
};
typedef X::value Xv;
#define CALL(function,param) printf(#function " (" #param ") = "); function(param());
void explicitCalls() {
printf("Explicit calls:\n");
CALL(foo<int>,int);
CALL(foo<X::value>,X::value);
CALL(foo<Z::value>,Z::value);
CALL(foo<XZ::value>,XZ::value);
CALL(foo<Xv>,Xv);
}
void implicitCalls() {
printf("Implicit calls:\n");
CALL(foo,int);
CALL(foo,X::value);
CALL(foo,Z::value);
CALL(foo,XZ::value);
CALL(foo,Xv);
}
int main() {
explicitCalls();
implicitCalls();
}
The output is:
Explicit calls:
foo<int> (int) = foo(T)
foo<X::value> (X::value) = foo(T::value)
foo<Z::value> (Z::value) = foo(T)
foo<XZ::value> (XZ::value) = foo(T)
foo<Xv> (Xv) = foo(T::value)
Implicit calls:
foo (int) = foo(T)
foo (X::value) = foo(T)
foo (Z::value) = foo(T)
foo (XZ::value) = foo(T)
foo (Xv) = foo(T)