Variadic template aliases as template arguments (part 2) - c++

This is a follow-up of another question. It refers to the same problem (I hope) but uses an entirely different example to illustrate it. The reason is that in the previous example only experimental GCC 4.9 failed with a compiler error. In this example, also Clang and GCC 4.8.1 fail in different ways: Clang produces an unexpected result and GCC 4.8.1 reports a different error message.
Answers to the previous question say more or less that the code is valid and the problem lies with the experimental version of GCC. But this result makes me a bit more sceptical. I have been troubled for months with problems that I suspect are related (or the same), and this is the first time I have a small concrete example to illustrate.
So, here is some code. First, some generic code that applies SFINAE to an arbitrary test as specified by a variadic template alias metafunction F:
#include <iostream>
using namespace std;
using _true = integral_constant <bool, true>;
using _false = integral_constant <bool, false>;
template <typename T> using pass = _true;
template <template <typename...> class F>
struct test
{
template <typename... A> static _false _(...);
template <typename... A> static pass <F <A...> > _(int);
};
template <template <typename...> class F, typename... A>
using sfinae = decltype(test <F>::template _<A...>(0));
Second, a specific test, checking if a given class has defined a type named type:
template <typename T> using type_of = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;
Finally, an example:
struct A { using type = double; };
int main()
{
cout << has_type <int>() << ", ";
cout << has_type <A>() << endl;
}
The expected result would be 0, 1. Clang says 0, 0. GCC 4.8.1 says
tst.cpp: In substitution of ‘template<class T> using type_of = typename T::type [with T = A ...]’:
tst.cpp:15:51: required from ‘struct test<type_of>’
tst.cpp:19:67: required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58: required from here
tst.cpp:23:56: error: ‘A ...’ is not a class, struct, or union type
template <typename T> using type_of = typename T::type;
^
and GCC 4.9 says
tst.cpp:19:67: required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58: required from here
tst.cpp:15:51: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using type_of = typename T::type’
template <typename... A> static pass <F <A...> > _(int);
^
(line numbers may vary). So, everything fails, in different ways.
Now, here is a workaround. Metafunction car picks the first type fom a given pack, and then the test is redefined as type_of2, now being variadic:
template <typename... T> struct car_t;
template <typename... T> using car = type_of <car_t <T...> >;
template <typename T, typename... Tn>
struct car_t <T, Tn...> { using type = T; };
template <typename... T> using type_of2 = typename car <T...>::type;
template <typename T> using has_type2 = sfinae <type_of2, T>;
int main()
{
cout << has_type2 <int>() << ", ";
cout << has_type2 <A>() << endl;
}
Now all three compilers say 0, 1 as expected. It is interesting that for any version of GCC we have to remove has_type (even if we don't use it) and leave only has_type2; otherwise we have similar error.
To wrap up: I see the problem with one template expecting a variadic template-parameter of the form
template <typename...> class F
where we actually give as input a non-variadic template alias of the form
template <typename T> using alias = // ... anything including T or not
and finally invoke F as if it was variadic:
F <A...>
Opinions so far say this is valid, but now it seems three compilers disagree. So the question is again: is it valid?
To me it matters because I have dozens of files of existing code based on the assumption that this is valid, and now I need a redesign anyway (since there are practical problems with these compilers) but the exact redesign will depend on the answer.

This does not answer the question whether the code above is valid, but is a quite pretty workaround that I have found by experimenting shortly after asking the question, and I think is useful to share.
All that is needed are the following definitions:
template <template <typename...> class F>
struct temp { };
template <typename... A, template <typename...> class F>
F <A...> subs_fun(temp <F>);
template <template <typename...> class F, typename... A>
using subs = decltype(subs_fun <A...>(temp <F>()));
then, wherever F <A...> would be problematic, replace it with subs <F, A...>. That's it. I cannot explain why, but it has worked in all cases so far.
For instance, in the SFINAE example of the question, just replace line
template <typename... A> static pass <F <A...> > _(int);
by
template <typename... A> static pass <subs <F, A...> > _(int);
This is a change at one point only, all remaining code stays the same. You don't need to redefine or wrap every template metafunction that with be used as F. Here's a live example.
If F <A...> is indeed valid and compilers support it eventually, it is again easy to switch back because changes are minimal.
I find this important because it allows specifying a SFINAE test in just two lines
template <typename T> using type_of = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;
and is completely generic. Typically, each such test needs at least 10 lines of code and implementations of <type_traits> are full of such code. In some cases such code blocks are defined as macros. With this solution, templates can do the job and macros are not needed.

I think the situation is pretty well standardized; C++11 14.3.3/1 says:
A template-argument for a template template-parameter shall be the name of a class template or an alias template, expressed as id-expression.

Related

SFINAE `std::void_t` class template specialisation [duplicate]

Are multiple class template specialisations valid, when each is distinct only between patterns involving template parameters in non-deduced contexts?
A common example of std::void_t uses it to define a trait which reveals whether a type has a member typedef called "type". Here, a single specialisation is employed. This could be extended to identify say whether a type has either a member typedef called "type1", or one called "type2". The C++1z code below compiles with GCC, but not Clang. Is it legal?
template <class, class = std::void_t<>>
struct has_members : std::false_type {};
template <class T>
struct has_members<T, std::void_t<typename T::type1>> : std::true_type {};
template <class T>
struct has_members<T, std::void_t<typename T::type2>> : std::true_type {};
There is a rule that partial specializations have to be more specialized than the primary template - both of your specializations follow that rule. But there isn't a rule that states that partial specializations can never be ambiguous. It's more that - if instantiation leads to ambiguous specialization, the program is ill-formed. But that ambiguous instantiation has to happen first!
It appears that clang is suffering from CWG 1558 here and is overly eager about substituting in void for std::void_t.
This is CWG 1980 almost exactly:
In an example like
template<typename T, typename U> using X = T;
template<typename T> X<void, typename T::type> f();
template<typename T> X<void, typename T::other> f();
it appears that the second declaration of f is a redeclaration of the first but distinguishable by SFINAE, i.e., equivalent but not functionally equivalent.
If you use the non-alias implementation of void_t:
template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;
then clang allows the two different specializations. Sure, instantiating has_members on a type that has both type1 and type2 typedefs errors, but that's expected.
I don't believe it's correct, or at least, not if we instantiate has_members with a type that has both type1 and type2 nested, the result would be two specializations that are
has_members<T, void>
which would not be valid. Until the code is instantiated I think it's ok, but clang is rejecting it early. On g++, your fails with this use-case, once instantiated:
struct X
{
using type1 = int;
using type2 = double;
};
int main() {
has_members<X>::value;
}
The error message is doesn't seem to describe the actual problem, but it at least is emitted:
<source>:20:21: error: incomplete type 'has_members<X>' used in nested name specifier
has_members<X>::value;
^~~~~
If you instantiate it with a type that has only type1 or type2 but not both,
then g++ compiles it cleanly. So it's objecting to the fact that the members are both present, causing conflicting instantiations of the template.
To get the disjunction, I think you'd want code like this:
template <class, class = std::void_t<>>
struct has_members : std::bool_constant<false> {};
template <class T>
struct has_members<T, std::enable_if_t<
std::disjunction<has_member_type1<T>, has_member_type2<T>>::value>> :
std::bool_constant<true> {};
This assumes you have traits to determine has_member_type1 and has_member_type2 already written.

Difference between `typename = enable_if_t<...>` and `enable_if_t<...,bool> = true`

Is there any difference between using typename = enable_if_t<...> and enable_if_t<...,bool> = true for SFINAE? I am asking specifically since I stumbled upon what seems like a bug: Compiler error with a fold expression in enable_if_t
So I got curious whether there is any actual difference between the two.
There are minor differences, but both can be used for SFINAE.
typename = enable_if_t<...> forms doesn't allow "simple" overloads:
template <typename T, typename = enable_if_t<cond<T>::value>>
void foo();
template <typename T, typename = enable_if_t<!cond<T>::value>>
void foo(); // Error: redeclaration of same function as default are not part of signature
// Both are just template <typename, typename> void foo()
enable_if_t<cond, bool> = true doesn't suffer of that:
template <typename T, enable_if_t<cond<T>::value, bool> = true>
void foo();
template <typename T, enable_if_t<!cond<T>::value, bool> = true>
void foo();
Another issue with typename = enable_if_t<...> is that usage might be hijacked:
template <typename T, typename = enable_if_t<cond<T>::value>>
void foo();
template <typename T, typename = enable_if_t<cond<T>::value>>
void bar(T);
foo<int>(); // Regular usage, SFINAE occurs
bar(42); // Regular usage, SFINAE occurs
bar<int>(42); // Possible usage, SFINAE still occurs
// But
foo<int, void>(); // No substitution fails here, so no SFINAE
bar<int, void>(42); // No substitution fails here, so no SFINAE
Yes, there is difference. The first one doesn't work, while the second works. The reason for that is that default template parameters are not part of function signature.
By "work" I mean that the first version doesn't remove the function from the set of candidate overloads, which is usually desired goal when enable_if is used.
An example can be found here (Courtesy of #NathanOliver):
http://coliru.stacked-crooked.com/a/a15a6f1d0eaff4ab

Check if class is a template specialization

I want to check if a class is a template specialization of another one. What I have tried is:
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
It works fine when all template parameters are type arguments but not when some are non-type arguments. For example it works with std::vector but not std::array (since the later accepts an non-type argument std::size_t).
It's important that the check is made at compile time. Also the solution must work for any template, not just vectors or arrays. That means that it can be any number of type arguments and any number of non-type arguments. For example it should work with template <class A, bool B, class C, int D, class... Args> class foo;
C++20 is a weird, weird world. Cross-checking is welcome as I'm a beginner with CTAD and not entirely sure I've covered all bases.
This solution uses SFINAE to check whether class template argument deduction (CTAD) succeeds between the requested class template and the mystery type. An additional is_same check is performed to prevent against unwanted conversions.
template <auto f>
struct is_specialization_of {
private:
template <class T>
static auto value_impl(int) -> std::is_same<T, decltype(f.template operator()<T>())>;
template <class T>
static auto value_impl(...) -> std::false_type;
public:
template <class T>
static constexpr bool value = decltype(value_impl<T>(0))::value;
};
// To replace std::declval which yields T&&
template <class T>
T declrval();
#define is_specialization_of(...) \
is_specialization_of<[]<class T>() -> decltype(__VA_ARGS__(declrval<T>())) { }>::value
// Usage
static_assert(is_specialization_of(std::array)<std::array<int, 4>>);
First caveat: Since we can't declare a parameter for the class template in any way without knowing its arguments, passing it around to where CTAD will be performed can only be done by jumping through some hoops. C++20 constexpr and template-friendly lambdas help a lot here, but the syntax is a mouthful, hence the helper macro.
Second caveat: this only works with movable types, as CTAD only works on object declarations, not reference declarations. Maybe a future proposal will allow things such as std::array &arr = t;, and then this will be fixed!
Actually fixed by remembering that C++17 has guaranteed copy-elision, which allows direct-initialization from a non-movable rvalue as is the case here!

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.

Class template specialization priority/ambiguity

While trying to implement a few things relying on variadic templates, I stumbled accross something I cannot explain. I boiled down the problem to the following code snippet:
template <typename ... Args>
struct A {};
template <template <typename...> class Z, typename T>
struct test;
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I'm more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I'm variadic!" << std::endl;
}
};
int main() {
test<A, A<int>>::foo();
}
Under gcc, it produces an error because it considers both specializations to be equally specialized when trying to instantiate test<A, A<int>>:
main.cpp: In function 'int main()':
main.cpp:25:24: error: ambiguous template instantiation for 'struct test<A, A<int> >'
test<A, A<int>>::foo();
^~
main.cpp:11:12: note: candidates are: template<template<class ...> class Z, class T> struct test<Z, Z<T> > [with Z = A; T = int]
struct test<Z, Z<T>> {
^~~~~~~~~~~~~
main.cpp:18:12: note: template<template<class ...> class Z, class T, class ... Args> struct test<Z, Z<T, Args ...> > [with Z = A; T = int; Args = {}]
struct test<Z, Z<T, Args...>> {
However, clang deems the first specialization "more specialized" (through partial ordering: see next section) as it compiles fine and prints:
I'm more specialized than the variadic spec, hehe!
A live demo can be found on Coliru. I also tried using gcc's HEAD version and got the same errors.
My question here is: since these two well-known compilers behave differently, which one is right and is this piece of code correct C++?
Standard interpretation (C++14 current draft)
From the sections §14.5.5.1 and $14.5.5.2 of the C++14 standard draft, partial ordering is triggered to determine which specialization should be chosen:
(1.2) — If more than one matching specialization is found, the partial order rules (14.5.5.2) are used to determine
whether one of the specializations is more specialized than the others. If none of the specializations
is more specialized than all of the other matching specializations, then the use of the class template is
ambiguous and the program is ill-formed.
Now according to §14.5.5.2, the class template specializations are transformed into function templates through this procedure:
For two class template partial specializations, the first is more specialized than the second if, given the
following rewrite to two function templates, the first function template is more specialized than the second
according to the ordering rules for function templates (14.5.6.2):
(1.1) — the first function template has the same template parameters as the first partial specialization and has
a single function parameter whose type is a class template specialization with the template arguments
of the first partial specialization, and
(1.2) — the second function template has the same template parameters as the second partial specialization
and has a single function parameter whose type is a class template specialization with the template
arguments of the second partial specialization.
Therefore, I tried to reproduce the issue with the function template overloads that the transformation described above should generate:
template <typename T>
void foo(T const&) {
std::cout << "Generic template\n";
}
template <template <typename ...> class Z, typename T>
void foo(Z<T> const&) {
std::cout << "Z<T>: most specialized overload for foo\n";
}
template <template <typename ...> class Z, typename T, typename ... Args>
void foo(Z<T, Args...> const&) {
std::cout << "Z<T, Args...>: variadic overload\n";
}
Now trying to use it like this:
template <typename ... Args>
struct A {};
int main() {
A<int> a;
foo(a);
}
yields a compilation error [ambiguous call] in both clang and gcc: live demo. I expected clang would at least have a behavior consistent with the class template case.
Then, this is my interpretation of the standard (which I seem to share with #Danh), so at this point we need a language-lawyer to confirm this.
Note: I browsed a bit LLVM's bug tracker and could not find a ticket for the behavior observed on function templates overloads in this question.
From temp.class.order:
For two class template partial specializations, the first is more specialized than the second if, given the following rewrite to two function templates, the first function template is more specialized than the second according to the ordering rules for function templates ([temp.func.order]):
Each of the two function templates has the same template parameters as the corresponding partial specialization.
Each function template has a single function parameter whose type is a class template specialization where the template arguments are the corresponding template parameters from the function template for each template argument in the template-argument-list of the simple-template-id of the partial specialization.
The order of:
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I'm more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I'm variadic!" << std::endl;
}
};
depends on the order of:
template <template <typename...> class Z, typename T>
void bar(test<Z, Z<T>>); // #1
template <template <typename...> class Z, typename T, typename ... Args>
void bar(test<Z, Z<T, Args...>>); // #2
From [temp.func.order]:
Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.
To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs ([temp.variadic]) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.
Using the transformed function template's function type, perform type deduction against the other template as described in [temp.deduct.partial].
By those paragraph, for any function transformed from any synthesized template Z0 and type T0, which can form #1, we can do type deduction with #2. But functions transformed from #2 with fictitious template Z2 with any type T2 and any non-empty set of Args2 can't be deduced from #1. #1 is obviously more specialized than #2.
clang++ is right in this case.
Actually, this one and this one are failed to compile (because of ambiguous) in both g++ and clang. It seems like both compilers have hard time with template template parameters. (The latter one is clearly ordered because its order is the same of no function call).