template function matching with specialization [duplicate] - c++

Consider the following code:
#include <type_traits>
template<template<class...> class T, class... U>
struct is_specialization_of : std::false_type{};
template<template<class...> class T, class... U>
struct is_specialization_of<T, T<U...>> : std::true_type{};
template<class T, class U = int>
struct test{};
// (1) ok
static_assert(is_specialization_of<test, test<int>>::value, "1");
template<class T>
using alias = test<T>;
// (2) fails
static_assert(is_specialization_of<alias, alias<int>>::value, "2");
int main()
{
}
Why does (2), i.e. static_assert that uses alias template, fail?
How does the template argument deduction process in (2) differ from the one in (1)?

This is CWG issue 1286. The question is: are alias and test equivalent? There used to be an example in [temp.type] which suggested that y and z have the same type here:
template<template<class> class TT> struct X { };
template<class> struct Y { };
template<class T> using Z = Y<T>;
X<Y> y;
X<Z> z;
The example was corrected as part of CWG defect 1244 - which indicated correctly that there is no wording in [temp.alias] that actually specifies that alias templates are equivalent to the templates they alias. The only wording there refers to equivalence of alias template specializations:
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.
The intent is apparently that y and z do have the same type in this example, meaning that Z and Y are actually equivalent. But unless and until the wording in the resolution is adopted, they are not. Today, alias and test are not equivalent but alias<int> and test<int> are. This means that is_specialization_of<alias, alias<int>> is is_specialization_of<alias, test<int>>, where alias is unique from test, which would not match your partial specialization and thus be false_type.
Moreover, even with the adoption of the wording in #1286, test and alias are still not equivalent for the obvious reason that test takes two template parameters and alias takes one template parameter. The example in the resolution wording mimics your example and clarifies the intent here:
template<typename T, U = T> struct A;
// ...
template<typename V>
using D = A<V>; // not equivalent to A:
// different number of parameters

I think that name of alias template without template arguments list is not equivalent to the name of associated type. Because standard specifies only one such situation:
14.5.7 Alias templates [temp.alias]
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. [Note: An alias template name is never deduced.—end note ]
and this works fine:
static_assert(is_specialization_of<test, alias<int>>::value, "2");

Related

How can I create deduction guides for template aliases in C++20?

Suppose I have a class/struct template together with an explicit deduction guide for its constructor. Let this class have two template parameters of which one can get deduced by the deduction guide, for the other it can not.
template <int Q, typename T>
struct Foo {
template <typename F>
Foo(F&&) { }
};
template <typename T>
using alias = T;
template <typename T>
struct alias2 { using type = T; };
template <int Q, typename F>
Foo(F&& f) -> Foo<Q, alias<F>>; // deduction guide, but cannot deduce Q yet
template <typename T>
using Bar = Foo<1, T>; // create alias that fixes Q
/* This should generate a deduction guide for Bar<T> by first
"copying" Foo's deduction guide, deducing from Foo<Q, alias<F>>
and Foo<1, T> that Q=1 and T=alias<F>=F, thus generating
<template F>
Bar(F&&) -> Bar<1, F>;
if this was correct syntax. */
int main() {
Bar f{ 5 };
}
If I now create an alias that will explicitly specify the formerly undeducable parameter, as far as I understand, the implicitly-generated deduction guide of this alias should be able to fully deduce both template arguments (by the rules of standard template argument deduction), even if one type is undeduced in the defining class template.
But what can I do in the scenario where I do not use alias, but alias2, i.e. change the deduction guide to
template <int Q, typename F>
Foo(F&& f) -> Foo<Q, typename alias2<F>::type>;
According to the documentation, this would now introduce a non-deduced context (as the template parameter appears left to a scope operator ::), so the template argument deduction for T=F should fail (which apparently does).
Question 1: If this theory is correct, is there something I can do about it? Suppose I do not want to use a trivial identity alias, but a more complex type transformation that will ultimatively have the shape of a typename transformation<Input>::result in the deduction guide.
Question 2: Even now, my theory fails when I remove Q entirely, as the following code will be accepted (by GCC-10/11):
template <typename T>
struct Foo {
template <typename F>
Foo(F&&) { }
};
template <typename T>
struct alias2 { using type = T; };
template <typename F>
Foo(F&& f) -> Foo<typename alias2<F>::type>;
template <typename T>
using Bar = Foo<T>;
template <typename T>
void some(typename alias2<T>::type) { }
int main() {
Bar f{ 5 };
}
Why is the compiler able to deduce T from F even if this is a non-deduced context?
To do what you want, C++ would have to be able to invert a Turing-complete subprogram.
Turing-complete programs are not only not-invertible, it isn't possible to determine if a given Turing-complete program is invertible or not. You can define sublanguages where they are all invertible, but those sublanguages lack Turing-complete power.
Deducing the Bar alias argument:
template <typename T>
using Bar = Foo<1, T>;
requires inverting the 2nd template argument alias<F> to find F. When alias is a trivial template alias, this is possible, permitted, and required by the C++ standard.
When alias2 evaluates is to a foo<F>::type, such a construct is capable of turing-complete computation. Rather than have compilers try to invert such a computation, the C++ standard uniformly says "don't try". It uses "dependent type" usually to block such an inversion attempt.
In your second case, Bar is a trivial alias of Foo. Foo has a deduction guide. That deduction guide tells how to get from F to T, so the compiler doesn't have to invert any potentially Turing-complete programs in order to determine T.
The C++ language has a bunch of wording to permit template aliases that are just renaming of parameters or the like to act as if they were the original type. Originally even a toy alias would block a bunch of this kind of deduction; but this was found to be a bad plan. So they added text to the standard to describe what kind of template aliases where "trivial" like that, and modified the wording of deduction rules so that they would be treated as transparent.
In order to invert an arbitrary Turing-complete program (in fact, almost any structurally non-trivial type transformation) during type deduction, you must explicitly give the inversion.
Once you have accepted that, it becomes battle with syntax, not a conceptual one.
I'm unaware of the current status of user-defined template deduction guides of alias templates. Last I heard it wasn't supported, but I haven't checked recently.

Specialization of class template parameter compiles in GCC, Clang and MSVC but GCC selects another specialization [duplicate]

Consider the following code:
#include <type_traits>
template<template<class...> class T, class... U>
struct is_specialization_of : std::false_type{};
template<template<class...> class T, class... U>
struct is_specialization_of<T, T<U...>> : std::true_type{};
template<class T, class U = int>
struct test{};
// (1) ok
static_assert(is_specialization_of<test, test<int>>::value, "1");
template<class T>
using alias = test<T>;
// (2) fails
static_assert(is_specialization_of<alias, alias<int>>::value, "2");
int main()
{
}
Why does (2), i.e. static_assert that uses alias template, fail?
How does the template argument deduction process in (2) differ from the one in (1)?
This is CWG issue 1286. The question is: are alias and test equivalent? There used to be an example in [temp.type] which suggested that y and z have the same type here:
template<template<class> class TT> struct X { };
template<class> struct Y { };
template<class T> using Z = Y<T>;
X<Y> y;
X<Z> z;
The example was corrected as part of CWG defect 1244 - which indicated correctly that there is no wording in [temp.alias] that actually specifies that alias templates are equivalent to the templates they alias. The only wording there refers to equivalence of alias template specializations:
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.
The intent is apparently that y and z do have the same type in this example, meaning that Z and Y are actually equivalent. But unless and until the wording in the resolution is adopted, they are not. Today, alias and test are not equivalent but alias<int> and test<int> are. This means that is_specialization_of<alias, alias<int>> is is_specialization_of<alias, test<int>>, where alias is unique from test, which would not match your partial specialization and thus be false_type.
Moreover, even with the adoption of the wording in #1286, test and alias are still not equivalent for the obvious reason that test takes two template parameters and alias takes one template parameter. The example in the resolution wording mimics your example and clarifies the intent here:
template<typename T, U = T> struct A;
// ...
template<typename V>
using D = A<V>; // not equivalent to A:
// different number of parameters
I think that name of alias template without template arguments list is not equivalent to the name of associated type. Because standard specifies only one such situation:
14.5.7 Alias templates [temp.alias]
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. [Note: An alias template name is never deduced.—end note ]
and this works fine:
static_assert(is_specialization_of<test, alias<int>>::value, "2");

Template aliases conflicting types. g++ compiles successfully while clang fails

I encountered a very strange compiler error. For some reason the posted code does compile properly with g++ (7.3.0) while clang (7.0.0) fails:
../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
freeFunc(new Func, dummyField);
^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
helper.func();
^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
^
Both compiler options were set to -std=c++14
template<typename T>
struct ConcreteData
{
T data;
};
template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
FieldData<T> someMember;
};
template<typename T, template<typename U> class FieldData>
struct SomeFunc
{
};
template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
ConcreteField<T, FieldData>& field)
{
// apply the func on data
(void)field; // silence compiler warning
delete func;
}
template<
typename ScalarType,
template<typename U> class FieldDataType,
template<typename U, template <typename X> class Data> class FieldType
>
struct Traits
{
using Scalar = ScalarType;
template<typename T>
using FieldData = FieldDataType<T>;
using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
// using Field = FieldType<Scalar, FieldData>; // using this line helps clang
};
template<typename Traits>
struct Helper
{
// alias all types given by trait for easier access
using Scalar = typename Traits::Scalar;
using Field = typename Traits::Field;
template<typename U>
using DataAlias = typename Traits::template FieldData<U>;
void func()
{
// using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang
Field dummyField;
freeFunc(new Func, dummyField);
}
};
int main()
{
using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
Helper<ConcreteTraits> helper;
helper.func();
return 0;
}
According to cppreference.com:
A type alias declaration introduces a name which can be used as a
synonym for the type denoted by type-id. It does not introduce a new
type and it cannot change the meaning of an existing type name. There
is no difference between a type alias declaration and typedef
declaration. This declaration may appear in block scope, class scope,
or namespace scope.
and
Alias templates are never deduced by template argument deduction when
deducing a template template parameter.
In my understanding both types (ConcreteData and FieldData) should be equivalent. Why is clang failing in this condition and why do both compiler fail when using the "second stage" alias? Which compiler is right according to the C++ standard? Is it a compiler bug or a subtle ambiguous interpretation of the C++14 standard?
Borrowing the minimal example of #Oktalist.
template <typename>
class T {};
template <typename _>
using U = T<_>;
template <template <typename> class X>
void f(A<X>, A<X>) {}
if you replace f by:
template <template <typename> class X, template <typename> class Y>
void f(A<X>, A<Y>) {}
the code no longer fail to compile. You can see that the problem is about equivalence of template parameters X and Y, they are deduced to different types.
The equivalence of types produced by alias template are only considered when referring to specialization of the alias, as is specified on [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
Using this rule and the rules for equivalence [temp.type]/1:
T<int> and U<int> are equivalent, so are X<T<int>> and Z<U<int>>, but this rule doesn't extend to the alias template U being equivalent to the class template T (by themselves, they aren't specializations).
This is the same scenario for the alias FieldData and the class template ConcreteData.
There are in fact two defect report, CWG-1286 and CWG-1244 that propose the equivalence extension for alias templates.

Weird behaviour comparing template type-aliases to types via template template parameters [duplicate]

Consider an alias template like the A in the code below. Now let B be an alias template of A.
In the code below these class templates are used as template arguments for a struct C which is only specialized for one typename (A). clang -std=c++11 exists with error: implicit instantiation of undefined template 'C<B>' indicating that another specialization for B is needed.
template<int N>
using A = int;
template<int N>
using B = A<N>;
template<template<int> class I>
struct C;
template<>
struct C<A> {};
int main() {
C<A> c;
C<B> d; // clang error: implicit instantiation
}
Why (if even) is it that - despite not allowing specializations of aliases - A and B are treated as different class templates? Is there a workaround allowing me to rename a lengthy template without incurring this problem?
This is CWG issue #1286, which deals with this example:
template<template<class> class TT> struct X { };
template<class> struct Y { };
template<class T> using Z = Y<T>;
X<Y> y;
X<Z> z;
questioning whether or not y and z have the same type.
Basically, according to the Standard, clang is correct in rejecting the code. All [temp.alias] tells us is:
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.
So while A<X> is equivalent to B<X> (for all X!), there is no wording that A is equivalent to B. But on some level that doesn't really make any sense since B and A should be equivalent. There is a proposed resolution that would make them so, but it has not yet been approved.

'typename' and alias templates

The following code compiles using both Clang and GCC, even though Foo_t<T>::Bar doesn't have typename in front of it:
struct Foo {
using Bar = int;
};
template<class...>
using Foo_t = Foo;
template<class T>
void f(){
Foo_t<T>::Bar b; // No typename!
}
int main(){
f<int>();
}
Should it compile?
Introduction
Foo_t<T>::Bar might look like a dependent-name, but it isn't since the template-arguments passed to the alias-declaration are not used when determining what the qualified-id Bar is referring to.
The code is well-formed.
What does the Standard (N3337) say?
14.5.7/2 Alias templates [temp.alias]
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.
A.6 Declarations [gram.dcl]
alias-declaration:
using identifier attribute-specifier-seq_opt = type-id ;
What is the Standard really saying?
Since there are no template-parameters in the type-id of Foo_t, the template alias-declaration is always directly equivalent to Foo, no matter what template-arguments we pass to it.
template<class... Ts>
using Foo_t = Foo;
// ^--- "Foo" = type-id
Replacing the usage of Foo_t<T> with the equivalence of the template alias-declaration leaves us with the following:
template<class T>
void f(){
Foo::Bar b; // ok, nothing here depends on `T`
}
With some more digging, this is CWG issue 1390.
The issue description is
According to 14.6.2.1 [temp.dep.type] paragraph 8, a type is dependent
(among other things) if it is
a simple-template-id in which either the template name is a template
parameter or any of the template arguments is a dependent type or an
expression that is type-dependent or value-dependent
This applies to alias template specializations, even if the resulting
type does not depend on the template argument:
struct B { typedef int type; };
template<typename> using foo = B;
template<typename T> void f() {
foo<T>::type * x; //error: typename required
}
Is a change to the rules for cases like this warranted?
And there's a note in that issue:
Notes from the October, 2012 meeting:
CWG agreed that no typename should be required in this case. In some
ways, an alias template specialization is like the current
instantiation and can be known at template definition time.
The issue is still in "drafting" status, but it looks like the compiler vendors are already implementing the intended resolution.