Templates instantiation confusion - c++

This is my code to check whether class has member function begin or not :
template<typename T> struct has_begin
{
struct dummy {typedef void const_iterator;};
typedef typename std::conditional< has_iterator<T>::yes, T, dummy>::type TType;
typedef typename TType::const_iterator Iter;
struct fallBack{ Iter begin() const ; Iter end() const;};
struct checker : T, fallBack {};
template <typename B, B> struct cht;
template<typename C> static char check(cht< Iter (fallBack::*)() const, &C::begin>*); // problem is here
template<typename C> static char (&check(...))[2];
public:
enum {no = (sizeof(check<checker>(0))==sizeof(char)),
yes=!no};
};
If I change second argument of cht in check(cht< Iter (fallBack::*)() const, &C::begin>*); to
&checker::begin , This doesn't changes the semantic of code since cht's second template argument is always checker due to this enum {no = (sizeof(check<checker>(0))==sizeof(char))
but code change results in error now which are :
prog.cpp: In instantiation of 'has_begin<std::vector<int> >':
prog.cpp:31:51: instantiated from here
prog.cpp:23:38: error: reference to 'has_begin<std::vector<int> >::checker::begin' is ambiguous
I want to know what is the reason behind this behavior.

from the Wikipedia article about SFINAE - Substitution Failure is Not An Error:
[...] when creating a candidate set for overload resolution, some (or all)
candidates of that set may be the result of substituting deduced
template arguments for the template parameters. If an error occurs
during substitution, the compiler removes the potential overload from
the candidate set instead of stopping with a compilation error [...]
In your code as posted, an ambiguity error occurs while instantiating the function template check with parameter C == typename has_begin<T>::checker, and that substitution leads to the error, so the instantiation is simply removed from the overload set.
If you change your code, a similar ambiguaty error occurs with &checker::begin.
This time, however, it is not the result of substituting the template parameter C for the check function template. The subsitution of the template parameter T of struct has_begin is not relevant for the SFINAE rule, as that template has already been successfully instantiated.

Related

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.

disable template member function if return type is an array

https://www.godbolt.org/z/_4aqsF:
template <typename T> struct Container
{
template <typename TPred> T find_if(TPred pred); // the culprit
};
template <typename T> Container<T> MakeContainer(T const &)
{
return Container<T>();
}
int main()
{
auto x = MakeContainer("Hello!");
}
gcc, clang and msvc apparently agree that this cannot compile because find_if would return an array.
(I would have assumed that the member template isn't instantiated since it doesn't get used - apparently, this simplistic view is wrong.)
Why does SFINAE not apply here?
Is there a way to exclude the member template for types where T is not a returnable type?
SFINAE is not in play because the members of the types produced in your MakeContainer return point are not examined during SFINAE of MakeContainer overloads.
SFINAE happens only in an immediate context. Bodies of types and functions are not in-scope and do not cause a subsitution failure.
template <typename T=char[7]> Container<char[7]> MakeContainer(char const (&)[7])
this signature is fine.
Once selected, the Container<char[7]> is instantiated and its methods parsed.
template <typename TPred> char[7] find_if(TPred pred); // the culprit
there is no TPred that could cause this find_if to be a valid method, so your program is ill formed no diagnostic required.
The correct fix is:
template <typename T> struct Container
{
template <typename TPred> T find_if(TPred pred); // the culprit
};
template <class T, std::size_t N> struct Container<T[N]>:
Container<std::array<T,N>>
{
using Container<std::array<T,N>>::Container;
};
of course, Container<std::array<T,N>> itself needs a very special find_if and probably constructors. But at least it doesn't break immediately.
SFINAE removes from overload sets the overloads which would be illegal during template argument deduction.
Here, the overload set contains only one candidate: MakeContainer<const char (&)[7]>. Template argument deduction ends here. No ambiguity. Everything's fine.
Then, the type Container<const char (&)[7]> is instantiated. And it produces a templated function (Container<const char (&)[7]>::find_if) whose signatures are illegal (all of them, since T is deduced in the context of find_if). SFINAE is not at play.
Now, you can add some SFINAE to your container's find_if function by making its return type depend on its template argument. For that, see max66's answer for that.
To use SFINAE on fidn_if you need to use dependent parameters of the function itself, here's a version that SFINAE on non-returnable types:
template <typename TPred, class U = T, typename std::enable_if<
std::is_same<T, U>::value
&& !std::is_abstract<U>::value
&& !std::is_function<U>::value
&& !std::is_array<U>::value
, bool>::type = true>
U find_if(TPred pred);
Try with
template <typename TPred, typename U = T>
U find_if (TPred pred); // the culprit
SFINAE, over methods, doesn't works with templates parameters of the class. Works over templates of the method itself. So you have to make the SFINAE substitution dependant from a template parameter of the method itself.
So not T but U.
If you fear that someone can "hijack" your function explicating the template types as follows
auto x = MakeContainer("Hello!");
x.find_if<int, int>(1);
you can impose that U and T are the same type
template <typename TPred, typename U = T>
typename std::enable_if<std::is_same<U, T>::value, U>::type
find_if (TPred pred) // the culprit

SFINAE: "enable_if cannot be used to disable this declaration"

Why can I not use enable_if in the following context?
I'd like to detect whether my templated object has the member function notify_exit
template <typename Queue>
class MyQueue
{
public:
auto notify_exit() -> typename std::enable_if<
has_member_function_notify_exit<Queue, void>::value,
void
>::type;
Queue queue_a;
};
Initialised with:
MyQueue<std::queue<int>> queue_a;
I keep getting (clang 6):
example.cpp:33:17: error: failed requirement 'has_member_function_notify_exit<queue<int, deque<int, allocator<int> > >, void>::value';
'enable_if' cannot be used to disable this declaration
has_member_function_notify_exit<Queue, void>::value,
or (g++ 5.4):
In instantiation of 'class MyQueue<std::queue<int> >':
33:35: required from here
22:14: error: no type named 'type' in 'struct std::enable_if<false, void>'
I've tried a bunch of different things, but can't figure out why I can't use enable_if to disable this function. Isn't this exactly what enable_if is for?
I've put a full example here (and cpp.sh link that often fails)
I've found similar Q/As on SO, but generally those were more complicated and attempting something different.
When you instantiate MyQueue<std::queue<int>> the template argument std::queue<int> gets substituted into the class template. In the member function declaration that leads to a use of typename std::enable_if<false, void>::type which does not exist. That's an error. You can't declare a function using a type that doesn't exist.
Correct uses of enable_if must depend on a template parameter that is deduced. During template argument deduction, if substituting the deduced template argument for the template parameter fails (i.e. a "substitution failure") then you don't get an immediate error, it just causes deduction to fail. If deduction fails, the function isn't a candidate for overload resolution (but any other overloads will still be considered).
But in your case the template argument is not deduced when calling the function, it's already known because it comes from the surrounding class template. That means that substitution failure is an error, because the function's declaration is ill-formed before you even try to perform overload resolution to call it.
You can fix your example by turning the function into a function template, so it has a template parameter that must be deduced:
template<typename T = Queue>
auto notify_exit() -> typename std::enable_if<
has_member_function_notify_exit<T, void>::value,
void
>::type;
Here the enable_if condition depends on T instead of Queue, so whether the ::type member exists or not isn't known until you try to substitute a template argument for T. The function template has a default template argument, so that if you just call notify_exit() without any template argument list, it's equivalent to notify_exit<Queue>(), which means the enable_if condition depends on Queue, as you originally wanted.
This function can be misused, as callers could invoke it as notify_exit<SomeOtherType>() to trick the enable_if condition into depending on the wrong type. If callers do that they deserve to get compilation errors.
Another way to make the code work would be to have a partial specialization of the entire class template, to simply remove the function when it's not wanted:
template <typename Queue,
bool Notifiable
= has_member_function_notify_exit<Queue, void>::value>
class MyQueue
{
public:
void notify_exit();
Queue queue_a;
};
// partial specialization for queues without a notify_exit member:
template <typename Queue>
class MyQueue<Queue, false>
{
public:
Queue queue_a;
};
You can avoid repeating the whole class definition twice in a few different ways. You could either hoist all the common code into a base class and only have the notify_exit() member added in the derived class that depends on it. Alternatively you can move just the conditional part into a base class, for example:
template <typename Queue,
bool Notifiable
= has_member_function_notify_exit<Queue, void>::value>
class MyQueueBase
{
public:
void notify_exit();
};
// partial specialization for queues without a notify_exit member:
template <typename Queue>
class MyQueueBase<Queue, false>
{ };
template<typename Queue>
class MyQueue : public MyQueueBase<Queue>
{
public:
// rest of the class ...
Queue queue_a;
};
template<typename Queue, bool Notifiable>
void MyQueueBase<Queue, Notifiable>::notify_exit()
{
static_cast<MyQueue<Queue>*>(this)->queue_a.notify_exit();
}
With C++20 and concept, you may use requires:
void notify_exit() requires has_member_function_notify_exit<Queue, void>::value;
Instantiating a template causes the member instantiation of all the declarations it contains. The declaration you provide is simply ill-formed at that point. Furthermore, SFINAE doesn't apply here, since we aren't resolving overloads when the class template is instantiated.
You need to make the member into something with a valid declaration and also make sure the check is delayed until overload resolution. We can do both by making notify_exit a template itself:
template<typename Q = Queue>
auto notify_exit() -> typename std::enable_if<
has_member_function_notify_exit<Q, void>::value,
void
>::type;
A working cpp.sh example

Unnamed class/typename in template arguments

I was looking through the documentation of SFINAE and there was this template declaration:
template<typename SomeType>
struct inner_type { typedef typename SomeType::type type; };
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename inner_type<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo(int) {}
Specifically, I'm asking about class = typename T::type. What's the point of declaring an unnamed class?
Because of the comment I thought that this will result in a compiler error when T doesn't have a member type, but that isn't the case, as foo<int, int, int>(0); compiles fine.
On the other hand
template<class T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
void foo(T t) {}
doesn't compile if T is signed, and compiles if T is unsigned.
What am I missing here?
foo<int, int, int>(0); compiles fine.
Because you specify the 2nd template argument, then the default template argument (i.e. typename T::type) won't be used, then won't trigger compile error.
If you just write foo<int>(0); to make the default template argument to be used, compile will fail.
LIVE
And it's same for your 2nd sample too.
What's the point of declaring an unnamed class?
Because the template parameter won't be used for the template implementation.

Parameter pack must be at the end of the parameter list... When and why?

I don't get the reason for which a parameter pack must be at the end of the parameter list if the latter is bound to a class, while the constraint is relaxed if the parameter list is part of a member method declaration.
In other terms, this one compiles:
class C {
template<typename T, typename... Args, typename S>
void fn() { }
};
The following one does not:
template<typename T, typename... Args, typename S>
class C { };
Why is the first case considered right and the second one is not?
I mean, if it's legal syntax, shouldn't it be in both the cases?
To be clear, the real problem is that I was defining a class similar to the following one:
template<typename T, typename... Args, typename Allocator>
class C { };
Having the allocator type as the last type would be appreciated, but I can work around it somehow (anyway, if you have a suggestion it's appreciated, maybe yours are far more elegant than mine!!).
That said, I got the error:
parameter pack 'Args' must be at the end of the template parameter list
So, I was just curious to fully understand why it's accepted in some cases, but it is not in some others.
Here is a similar question, but it simply explains how to solve the problem and that was quite clear to me.
It is valid for function templates but only when argument deduction can help the compiler resolve the template parameters, as it stands your function template example is virtually useless because
template<typename T, typename... Args, typename S> void fn() { }
int main() { fn<int, int, int>(); }
test.cpp: In function 'int main()':
test.cpp:2:32: error: no matching function for call to 'fn()'
int main() { fn<int, int, int>(); }
^
test.cpp:1:57: note: candidate: template<class T, class ... Args, class S> void fn()
template<typename T, typename... Args, typename S> void fn() { }
^
test.cpp:1:57: note: template argument deduction/substitution failed:
test.cpp:2:32: note: couldn't deduce template parameter 'S'
int main() { fn<int, int, int>(); }
the compiler has no way of determining which template parameters belong to the parameter pack, and which to S. In fact as #T.C. points out it should actually be a syntax error because a function template defined in this manner cannot ever be instantiated.
A more useful function template would be something like
template<typename T, typename... Args, typename S> void fn(S s) { }
as now the compiler is able to unambiguously match the function parameter s with the template type S, with the side effect that S will always be deduced - all explicit template parameters after the first will belong to Args.
None of this works for (primary) class templates, parameters aren't deduced and it's expressly forbidden:
From draft n4567
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf
[temp.param] / 11
[...]If a template-parameter of a primary class template or alias
template is a template parameter pack, it shall be the last
template-parameter.[...]
(if they were deduced it would be ambiguous as in the function template example).
The first one is not right. The compiler is just buggy and failed to diagnose it. [temp.param]/11:
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 of the function template or has a
default argument (14.8.2).
If the function type T(Args...) is meaningful to the end-user, one way to fix this would be to use a partial specialization instead:
template<class F, class Alloc> class C; //undefined
template<class T, class... Args, class Alloc>
class C<T(Args...), Alloc> {
// implementation
};
Depending on the actual requirements, type-erasing the allocator might also be worth considering.