template < int ...Indices>
class T1 {
template <int _1, int _2>
class T2;
};
template <int ...Indices>
template <int _1>
class T1<Indices...>::T2<_1, sizeof...(Indices)> {};
//^--error: non-type template argument depends on a template parameter of the partial specialization
compiles on gcc 4.5+ but neither on clang 3.1 nor icc, both complaining about the usage of sizeof...(Indices).
Is it just a not-yet implemented feature in the latter compilers or some special case?
Thanks,
Buote
The standard says in [temp.class.spec] paragraph 8 that
Within the argument list of a class template partial specialization, the following restrictions apply:
— A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier. [ Example:
template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {}; // error
template <int I, int J> struct B {};
template <int I> struct B<I, I> {}; // OK
— end example ]
The purpose of the rule is to disallow partial specializations based on non-trivial expressions like those in the example, and sizeof...(Indices) is not a simple identifier so maybe clang and ICC are right to reject it. I'm not sure which compiler is right, to be honest. I suggest reporting a bug to one of the compilers and if they say their implementation is correct report it to the others for interpreting it differently!
You could attempt to try:
template < int ...Indices>
class T1
{
static const int index_size = sizeof...(Indices);
template <int _1, int _2>
class T2;
};
template <int ...Indices>
template <int _1>
class T1<Indices...>::T2<_1, T1<Indices...>::index_size> {};
and see if the compiler issue is with the actual sizeof... operator, or if it's with the way it's being used in the template declaration.
Related
Can we have variadic concepts before variadic template parameters?
I meant, is following legal:
template <class, std::size_t> concept Any = true;
template <class> struct n_ary;
template <std::size_t... Is>
struct n_ary<std::index_sequence<Is...>>
{
template <Any<Is>... Ls, typename ... Ts>
void operator()(Ls..., Ts...) {}
};
Demo (accepted only by clang)
Note: Without the extra Ts, it is accepted by all compilers Demo.
This is probably in violation of [temp.param] section 14:
... A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list ([dcl.fct]) of the function template or has a default argument ([temp.deduct]). ...
It includes this example:
template<class T1 = int, class T2> class B; // error
// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { } // error
template<class... T, class U> void g() { } // error
However, cases like f(T..., U...) are currently accepted by compilers, which see one (or both) parameter packs as empty.
By the the example in section 5 about type constraints,
template<C3<int>... T> struct s5; // associates (C3<T, int> && ...)
the syntax in your example should be "functionally equivalent, but not equivalent" to template<typename... Ls, typename... Ts> requires (Any<Ls,Is> && ...). The expansion rules [temp.variadic] require that Ls and Is have the same size, which is why gcc and MSVC ultimately reject the call in your example. Interestingly, if you use the functionally equivalent syntax, clang does join the other compilers in rejecting it.
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.
This program
#include <iostream>
template <int I>
struct A
{
A() { std::cout << "A<I>()\n"; }
};
template <int I>
struct A<I + 5>
{
A() { std::cout << "A<I + 5>()\n"; }
};
int main()
{
return 0;
}
is compiled neither by gcc HEAD 10.0.0 20190 nor by clang HEAD 10.0.0.
For example the gcc compiler issues error
prog.cc:10:8: error: template argument '(I + 5)' involves template parameter(s)
10 | struct A<I + 5>
| ^~~~~~~~
Is there a wrong class template partial specialization?
This is not a valid partial specialization (though the error could be better). The clause we are not inline with is the following:
[temp.class.spec]
8 Within the argument list of a class template partial
specialization, the following restrictions apply:
The specialization shall be more specialized than the primary template.
"The specialization is not more specialized!?" I suppose you think. But that is indeed the case. The rules for determining which is "more specialized" are described in [temp.class.order]. The gist of it is that we are to consider two hypothetical function template overloads:
template <int I>
struct A { /* ... */ };
template<int I>
void foo(A<I>); //1
template<int I>
void foo(A<I + 5>); //2
We then perform overload resolution and partial ordering of the function templates. If #2 wins, it's more specialized and your declaration is legal. If it doesn't win, the declaration is invalid. Partial ordering is done by cooking up some arguments and doing template argument deduction of one template against another. So suppose we start with comparing the first to the second (I'll rename them for simplicity, but they are still overloads):
void foo1(A<0>); -> void foo2(A<I + 5>);
Does the argument deduction succeed here? It doesn't. I + 5 is a non-deduced context:
[temp.deduct.type]
The non-deduced contexts are:
5.3 - A non-type template argument or an array bound in which a
subexpression references a template parameter.
The I references a template parameter, so I + 5 is a non-deduced context. Therefore template argument deduction fails in this direction.
Let's try the other direction. Again we cook up an argument
void foo2(A<1 + 5>); = void foo2(A<6>); -> void foo1(A<I>);
Deduction obviously succeeds here. So according to the rules of function template partial ordering, foo1 is more specialized than foo2. This means that our primary is really more specialized than our "partial specialization", making the partial specialization ill-formed.
Looking into temp.class.spec.match/3, we have
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.
with the example
template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {}; // error
template <int I> struct A<I, I> {}; // OK
template <int I, int J, int K> struct B {};
template <int I> struct B<I, I*2, 2> {}; // OK
Clang's error message echoes this:
error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
(See #StoryTeller's answer for why this deduction fails in your code, I won't duplicate that here.)
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!
I can use std::experimental::is_detected to check whether chips1_t can be instantiated with float* (which it can't) as shown in the static_assert below:
#include <experimental/type_traits>
#include <type_traits>
template <typename, typename = void>
struct chips1;
template <typename T>
struct chips1<T*,std::enable_if_t<std::is_same<T,int>::value>> {
using type = int;
};
template <typename T> using chips1_t = typename chips1<T>::type;
static_assert(!std::experimental::is_detected<chips1_t,float*>::value,"");
If I then try a check using chips2_t, a similar static_assert, shown below, will produce a compilation error; regarding the missing type member. Can anyone tell me why?
struct Base {};
template <typename>
struct chips2;
template <typename T>
struct chips2<T*> : std::enable_if_t<std::is_same<T,int>::value,Base> {
using type = int;
};
template <typename T> using chips2_t = typename chips2<T>::type;
static_assert(!std::experimental::is_detected<chips2_t,float*>::value,"");
In the second case, the base class is not part of the template type, so it's not considered during SFINAE (It's a dependent base class)
Instantiating
template <typename T>
struct chips2<T*>
Succeeds, and then compilation fails because it derives from std::enable_if_t<std::is_same<T,int>::value,Base> which becomes an ill-formed expression.
You cannot specialize templates by their base class*.
For example, you couldn't have two competing specializations like:
template <typename T>
struct chips2<T*> : std::enable_if_t<std::is_same<T,int>::value,Base> {
using type = int;
};
template <typename T>
struct chips2<T*> : std::enable_if_t<!std::is_same<T,int>::value,Base> {
using type = float;
};
They would be considered identical from a specialization standpoint (i.e., they are identical from a naming standpoint, so the compiler will consider the second one to be a redeclaration of the first one). The only part that matters is everything up to the colon :
In the first case, your enable_if is directly part of the template specialization, so SFINAE works properly.
Another note
In the second one, you've effectively made it impossible to instantiate chips2 with anything other than int*, see the following example:
struct Base {};
template <typename>
struct chips2
{};
template <typename T>
struct chips2<T*>
: std::enable_if_t<std::is_same<T,int>::value, Base>
{};
int main(){
chips2<float*> c;
}
You might be inclined to think that SFINAE will choose the base class template for c, but in reality it chooses the specialization chips2<T*> because of what I said above, and compilation fails.
*Relevant standardese at [temp.spec]
the name of the class that is explicitly specialized shall be a simple-template-id.
Where a simple-template-id is of the form:
template-name < template-argument-listopt >
e.g. chips2<T*>. Note that there is no option to also include the class it derives from