Type traits and template overload resolution - c++

I'm writing a type trait to check that a given type is a specific container and I came across the following situation:
#include <array>
#include <cstddef>
template<typename T>
struct is_char_array_t final: std::false_type {};
template<std::size_t N>
struct is_char_array_t<std::array<char, N>> final: std::true_type {};
int main(){
if constexpr (is_char_array_t<42>) {
return 2;
}
}
This code doesn't compile, the error it gives is:
<source>: In function 'int main()':
<source>:10:37: error: type/value mismatch at argument 1 in template parameter list for 'template<class T> struct is_char_array_t'
10 | if constexpr (is_char_array_t<42>) {
| ^
<source>:10:37: note: expected a type, got '42'
<source>:10:38: error: expected unqualified-id before ')' token
10 | if constexpr (is_char_array_t<42>) {
| ^
ASM generation compiler returned: 1
<source>: In function 'int main()':
<source>:10:37: error: type/value mismatch at argument 1 in template parameter list for 'template<class T> struct is_char_array_t'
10 | if constexpr (is_char_array_t<42>) {
| ^
<source>:10:37: note: expected a type, got '42'
<source>:10:38: error: expected unqualified-id before ')' token
10 | if constexpr (is_char_array_t<42>) {
| ^
Execution build compiler returned: 1
The behavior is what I want, since it doesn't let anything that isn't a std::array<char, N> match the trait, but I'm left wondering why it works.
I would think that the template overload for std::size_t is a better match for the value 42 than the template that requires a type. So why doesn't the second, more specialized template match the given value?
Here's a godbolt with the example: https://godbolt.org/z/KdKvrPsx3

This is the template:
template<typename T>
struct is_char_array_t final: std::false_type {}; // (I)
And this is a specialization:
template<std::size_t N>
struct is_char_array_t<std::array<char, N>> final: std::true_type {};
The specialization is not a new template. It does not change the fact that is_char_array has one type argument. It merely specializes the primary template (I) for the case that T matches std::array<char,N>. The specialization starts with template <std::size_t N> because N is a "free parameter", you do not care what N is for the specialization as long as the type T is a std::array<char,N> for some N. In other words, it is a partial specialization. A full specialization would start with template <> and have no such "free parameter". You can identify the specialization by the is_char_array_t<std::array<char, N>> where the primary template only mentions the name of the template.
Your code does not really "work". The trait is used to yield either true or false, depending on the type that is used to instantiate it. Using a value is an error.

The primary template (the first one in your code) declares is_char_array_t as such:
template<typename T>
struct is_char_array_t;
That means is_char_array_t is a class template expecting exactly one template argument which should be a type template argument.
You are trying to give it 42 as template argument, which is a value, not a type. Therefore is_char_array_t<42> is wrong, no matter how is_char_array_t is implemented or partially specialized. Class templates also can't be overloaded, so this can never change.
Also, it is clearly not working. If it was working, then there wouldn't be any error message. if constexpr's condition would simply evaluate to false and the branch wouldn't be taken, so that the resulting program end with 0 status code instead.

Related

constraints with c++20 concepts

I'm trying to understand C++20 concepts, in particular the example from here. Why is it an error if we're templatizing f with a concept that's stricter than allowed? In other words doesn't Integral4 also satisfy the Integral concept?
#include <type_traits>
#include <concepts>
template<typename T>
concept Integral = std::integral<T>;
template<typename T>
concept Integral4 = Integral<T> && sizeof(T) == 4;
template<template<Integral T1> typename T>
void f(){
}
template<typename T>
struct S1{};
template<Integral T>
struct S2{};
template<Integral4 T>
struct S3{};
void test(){
f<S1>(); // OK
f<S2>(); // OK
// error, S3 is constrained by Integral4 which is more constrained than
// f()'s Integral
f<S3>();
}
Error
<source>: In function 'void test()':
<source>:28:10: error: no matching function for call to 'f<template<class T> requires Integral4<T> struct S3>()'
28 | f<S3>();
| ~~~~~^~
<source>:11:6: note: candidate: 'template<template<class T1> class requires Integral<T1> T> void f()'
11 | void f(){
| ^
<source>:11:6: note: template argument deduction/substitution failed:
<source>:28:10: error: constraint mismatch at argument 1 in template parameter list for 'template<template<class T1> class requires Integral<T1> T> void f()'
28 | f<S3>();
| ~~~~~^~
<source>:28:10: note: expected 'template<class T1> class requires Integral<T1> T' but got 'template<class T> requires Integral4<T> struct S3'
Compiler returned: 1
f takes a template template parameter that is constrained on Integral. This means that f is allowed to use any type T which satisfies Integral on this template.
For example, short.
S3 is a type constrained on Integral4, which subsumes Integral. This means that, while any type U which satisfies Integral4 will also satisfy Integral, the reverse is not true. There are some types T which satisfy Integral but not Integral4
short for example is Integral, but it is unlikely to satisfy Integral4.
But f has the right to use short on the template it is provided, because that's what its signature says it can do. Since S3 provided only allows a subset of the types that f's signature allows it to use, you get a compile error.

Why does this code with SFINAE compiles error, even though there is a template that can match

The code is as follows.
#include <tuple>
#include <array>
template <typename T, typename Type>
struct Vec {
using value_type = T;
static constexpr size_t size() { return Type::size; }
};
template <size_t Size>
struct Const {
static constexpr size_t size = Size;
};
template <class T, class Type, class = void>
struct vec_size_impl {};
template <class T, class Type>
struct vec_size_impl<T, Type, std::enable_if_t<std::is_arithmetic_v<T>>>
: std::integral_constant<size_t, Type::size> {};
template <class T, class Type>
inline constexpr size_t Vec_size_v = vec_size_impl<T, Type>::value;
template<class T, class Type, size_t... Sizes>
// template<size_t... Sizes, class T, class Type>
std::enable_if_t<
((0 + ... + Sizes) == Vec_size_v<T, Type>),
std::tuple<Vec<T, Const<Sizes>>...>
> split(const Vec<T, Type>&) noexcept {
return std::make_tuple<Vec<T, Const<Sizes>>...>();
}
template<class V, class Type>
std::enable_if_t<
(Vec_size_v<typename V::value_type, Type> % V::size() == 0),
std::array<V, Vec_size_v<typename V::value_type, Type> / V::size()>
> split(const Vec<typename V::value_type, Type>&) noexcept {
return std::array<V, Vec_size_v<typename V::value_type, Type> / V::size()>();
}
int main() {
Vec<int, Const<6>> a;
split<Vec<int, Const<2>>, Const<6>>(a);
return 0;
}
Here (I think) the second split() can be matched, but I still got a compile error for the substitution fail of the first template. What is the reason for this? I have not yet found an entry in the C++ standard that can explain this problem. (This appears to be related to variadic templates, because if I modify the order of the template parameters and put the parameter pack in the first place, the code would compile correctly.)
SFINAE applies only if the invalid type or expression resulting from a use of a template parameter appears within:
a template parameter declaration within the same template parameter list
a default template argument which is used (or will be if overload resolution or class partial specialization matching selects the template)
for a function template, the declared type of the function (including its function parameter types, return type, or exception specification, but not when deducing a placeholder return type from return statements),
for a function template, its explicit(constant-expression) specifier, if any
for a class template partial specialization, the template arguments specified after the template name
See [temp.deduct]/8 - this is the "immediate context" rule.
Substitution of all type aliases and type alias templates happens essentially "before" template argument substitution, since [temp.alias]/2 says a use of the alias template is always equivalent to its substitution. For example, this explains why SFINAE applies to the ::type member lookup in a std::enable_if_t within a function type - it is equivalent to the written-out typedef std::enable_if<...>::type, so when this forms an invalid type, it's considered to be in the "immediate context" of the function template argument substitution. Type aliases don't actually get "instantiated" at all like function templates, class templates, and variable templates do.
When overload resolution considers the first split template, it tries to get the value of Vec_size_v<Vec<int, Const<2>>, Const<6>>, which causes an implicit instantiation of that specialization of the variable template. The evaluation of that variable template's initializer is within that variable template instantiation, not within the function template's function type, so SFINAE does not apply and the variable template has an error, even though it happened during a template argument deduction for overload resolution.
The obvious workaround, though probably not what you want, is to require the longer Vec_size<T, Type>::value instead of Vec_size_v<T, Type>.
Or you could give the primary template for vec_size_impl a static value member. But it doesn't actually need to have a numeric type: if you do
template <class T, class Type, class = void>
struct vec_size_impl
{
struct none_t {};
static constexpr none_t value;
};
// partial specialization as before
template <class T, class Type>
inline constexpr auto Vec_size = vec_size_impl<T, Type>::value;
then the same declaration of the first split would get an actual valid constant expression for its Vec_size_v use, but an invalid expression (0 + ... + Sizes) == Vec_size_v<T, Type> since there's no matching operator==. But this invalid expression is within the function template's function type, so then SFINAE can discard the function template from the overload resolution process.
Thanks and the question is solved now. The error message is as follows:
cxx.cpp:24:62: error: no member named 'value' in 'vec_size_impl<Vec<int, Const<2>>, Const<6>>'
inline constexpr size_t Vec_size_v = vec_size_impl<T, Type>::value;
~~~~~~~~~~~~~~~~~~~~~~~~^
cxx.cpp:29:27: note: in instantiation of variable template specialization 'Vec_size_v<Vec<int, Const<2>>, Const<6>>' requested here
((0 + ... + Sizes) == Vec_size_v<T, Type>),
^
cxx.cpp:46:3: note: while substituting explicitly-specified template arguments into function template 'split'
split<Vec<int, Const<2>>, Const<6>>(a);
^
and the error is because the substitution failure is a hard error, since the deduction of Vec_size_impl::value is not in the immediate context. The question can be solved by replacing the Vec_size_v<T, Type> by Vec_size_impl<T, Type>::value in the enable_if.

Class template partial specialization: compiler error

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.)

How to deduce type of `T` from a pointer to member function?

I have a template, more or less like this:
template<typename T,void (T::*F)()>
struct Foo{
/* ... do some stuff with the member function pointer ...*/
//... e.g.
T foo(){
T t;
t.*F;
return t;
};
it works, but I dont like the way I have to instantiate it:
Foo<SomeVeryLongClassName,&SomeVeryLongClassName::AnEvenLongerMemberFunctionName> f;
Is there some way I can make the template deduce T?
I was thinking of a template method that I could call like this:
getFoo(&SomeVeryLongClassName::AnEvenLongerMemberFunctionName);
or, as I will mainly use Foo inside T, that would be just
getFoo(AnEvenLongerMemberFunctionName);
I tried this
#include <iostream>
template <typename T,void (T::*MEMFUN)()>
struct Foo{};
template <typename T,void (T::*MEMFUN)()>
Foo<typename T,typename MEMFUN> getFoo(MEMFUN f){
return Foo<typename T,typename MEMFUN>();
}
struct Bar { void test(){ std::cout << "MUH" << std::endl;} };
int main (){ getFoo(&Bar::test); }
The error messages are actually quite clear, but I dont understand them at all...
templateExample.cpp:9:28: error: wrong number of template arguments (1, should be 2)
Foo<typename T,typename MEMFUN>
^
templateExample.cpp:4:8: error: provided for ‘template<class T, void (T::* MEMFUN)()> struct Foo’
struct Foo{
^
templateExample.cpp:10:7: error: invalid type in declaration before ‘(’ token
getFoo(MEMFUN f){
^
templateExample.cpp:10:7: error: template declaration of ‘int getFoo’
templateExample.cpp:10:15: error: expected ‘)’ before ‘f’
getFoo(MEMFUN f){
^
templateExample.cpp: In function ‘int main()’:
templateExample.cpp:20:20: error: ‘getFoo’ was not declared in this scope
getFoo(&Bar::test);
...why "wrong number of template arguments (1, should be 2)" ?
How can I help the compiler to deduce T when instantiating a Foo ?
Is it possible with only pre-C++11?
PS: this is very close to being a dupe, but I really need to know the type of T and not just call the member function (e.g. I need to create an instance).
In C++17 we have non-type template parameters with deduced types:
template <auto> struct Foo;
template <typename T, void (T::*MF)()> struct Foo<MF> {
// ...
};
Usage: Foo<&X::f>
You can also directly use template <auto X> and either keep using auto inside your template or use decltype(X) to get at the type of the non-type parameter.
Prior to C++17, you could try to perform deduction via some contortions involving helper class templates with member function templates and decltype.
The gory details:
If you define a function template template <typename T, void(T::*MF)()> Foo<T, MF> f(MF);, where Foo is your old-style class template (like template <typename T, void (T::*MF)()> class Foo;), then you can use decltype(f(&X::h)) to deduce the desired type Foo<X, &X::h> without having to repeat X. The price is that you either need to say decltype everywhere, or you wrap that in a macro.

g++ and clang++ different behaviour with integral template parameter

I have the following C++11 code.
#include <type_traits>
using IntType = unsigned long long;
template <IntType N> struct Int {};
template <class T>
struct is_int : std::false_type {};
template <long long N>
struct is_int<Int<N>> : std::true_type {};
int main()
{
static_assert (is_int<Int<0>>::value, "");
return 0;
}
Clang++ 3.3 compiles the code but on g++ 4.8.2 static assertion fails
$ g++ -std=c++11 main.cpp
main.cpp: In function ‘int main()’:
main.cpp:15:5: error: static assertion failed:
static_assert (is_int<Int<0>>::value, "");
^
$
The problem is caused by different integral template parameters.
Which compiler is right in this case?
The surprise
This is a subtle Clang bug, deeply buried in the Standard. The problem is that in almost all cases, non-type template arguments can be converted to the type of the template parameter. E.g. the expression Int<0> has an int literal argument of value 0 that is being converted to the type unsigned long long of the template parameter N.
14.8.2 Template argument deduction [temp.deduct]/2 2nd bullet
-- Non-type arguments must match the types of the corresponding non-type
template parameters, or must be convertible to the types of the
corresponding non-type parameters as specified in 14.3.2, otherwise
type deduction fails.
Since your class template is_int<T> has a partial specialization, we need to look at
14.5.5.1 Matching of class template partial specializations [temp.class.spec.match]
1 When a class template is used in a context that requires an
instantiation of the class, it is necessary to determine whether the
instantiation is to be generated using the primary template or one of
the partial specializations. This is done by matching the template
arguments of the class template specialization with the template
argument lists of the partial specializations.
2 A partial specialization matches a given actual template argument
list if the template arguments of the partial specialization can be
deduced from the actual template argument list (14.8.2).
So it would seem that we can proceed to the earlier quote of 14.8.2/2 2nd bullet and match the second specialization (although in that case an even more complicated overload resolution game would have to be played).
The resolution
However, it turns out (as mentioned by #DyP in the comments) that another clause in the Standard supersedes this:
14.8.2.5 Deducing template arguments from a type [temp.deduct.type]
17 If, in the declaration of a function template with a non-type
template-parameter, the non-type templateparameter is used in an
expression in the function parameter-list and, if the corresponding
template-argument is deduced, the template-argument type shall match
the type of the template-parameter exactly, except that a
template-argument deduced from an array bound may be of any integral
type. [ Example:
template<int i> class A { / ... / };
template<short s> void f(A<s>);
void k1() {
A<1> a;
f(a); // error: deduction fails for conversion from int to short
f<1>(a); // OK
}
The upshot is that the partial specialization of is_int cannot be deduced because it does not take the exact same type (unsigned long long vs long long) as the Int class template's formal non-type template parameter.
You can resolve this by giving the non-type template parameter N in the partial specialization of is_int the same type as the non-type parameter N in the primary template Int.
template <IntType N>
// ^^^^^^^^
struct is_int<Int<N>> : std::true_type {};
Live Example.
Clang is being inconsistent. Since it accepts your code, I'm expecting the following code must output f(Int<long long>) instead of f(T):
using IntType = unsigned long long;
template <IntType N> struct Int {};
template<typename T>
void f(T) { std::cout << "f(T)" << std::endl; }
template<long long N>
void f(Int<N>) { std::cout << "f(Int<long long>)" << std::endl; }
int main()
{
f(Int<0>{});
}
But surprisingly, it outputs this (online demo):
f(T)
That shows Int<0> does NOT match with the second overload which accepts the argument as Int<N>. If that is so, then why does it match with Int<N> when it is used as template argument to the class template (in your case)?
My conclusion:
If Clang is correct in my case, then it is incorrect in your case.
If Clang is correct in your case, then it is incorrrect in my case.
Either way, Clang seems to have bug.
GCC, on the other hand, is consistent at least. That doesn't prove though that it doesn't have bug — it might mean that it has bug in both cases! Unless someone comes up with the standardese and showing it has bug too, I'm going to trust GCC in this case.