Partial specialization and the need for std::void_t<> - c++

One for the language lawyers....
I'm playing around with SFINAE and TMP, trying to get a deeper understanding.
Consider the following code, a naive implementation of std::is_default_constructible
#include <type_traits>
template <typename T, typename = void> struct is_default_constructable : std::false_type {};
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
class NC { NC(int); }; // Not default constructable
#include <iostream>
int main(int, char **)
{
std::cout << "int is_default_constructible? " << is_default_constructable<int>() << std::endl;
std::cout << "NC is_default_constructible? " << is_default_constructable<NC>() << std::endl;
}
This compiles fine, but doesn't actually work, it returns false for all types.
For the NC case, this is as I'd expect, T() is not well-formed so that specialization is discarded due to SFINAE and the primary template (false_type) is used. But for the int case, I'd expect the specialization to be used as decltype(T()) is valid and equivalent to T.
If, based on the actual code in <type_traits>, I change the specialization to
template <typename T> using wrap = void;
template <typename T> struct is_default_constructable<T, wrap<decltype(T())> > : std::true_type {};
(i.e. wrap the second template parameter in a mockup of std::void_t<> which forces the second type to be void), this works as expected.
Even more curious, variations of this scheme using types other than void as the default type in the primary template or wrap<> also fail, unless the two types are the same.
Can someone explain why the type of wrap<> and the second template argument default type need to be the same in order for the specialization to be selected?
(I'm using "g++ -Wall --std=c++17" with g++ version 6.3, but I think this is not compiler-related.)

This is not a consequence of SFINAE or partial specialization ordering, but due to the use of default template parameters. Informally, the reason is that the application of default template parameters happens before the search for template definitions, including possible specializations.
So in the above case, the code that says is_default_constructable<int> is actually requesting to instantiate a template is_default_constructable<int, void> after applying the default second parameter. Then possible definitions are considered.
The "primary" template definition clearly matches and is included.
The given partial specialization
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};
is actually defining is_default_constructable<int, int> which does not match the requested is_default_constructable<int, void> so the specialization is ignored, even if the substitution succeeds.
This leaves the primary definition (inheriting false_type) as the only viable definition so it is chosen.
When the specialization has wrap<> (or std::void_t<>) to force the second arg to a void, the specialization is defining is_default_constructable<int, void> which matches the request. This definition (asuming the substitution succeeds, i.e. T() is well formed) is more specialized than the primary definition (according to super-complicated rules for ordering specializations), so it is chosen.
As an aside, the above naive implementations probably don't work as expected when T is a reference type or other corner cases, which is a good reason to use the standard library versions of all this. Them standards committee people are way smarter than I am and have already thought of all these things.
This answer and this answer to somewhat-related questions have more detailed information that set me right.
And, yes, I can't spell constructible, assuming that's even a word. Which is another good reason to use the standard library.

Related

Why do partial and full C++ template specializations, that look almost the same, produce different results?

I haven't written many C++ templates till recently, but I'm trying to take a deep dive into them. Fixing one developer's code I managed to produce some compiler behavior that I can't explain. Here is the simplified program. ( When I try to simplify more, I lose that "strange" behavior). Let's think of some class Depender, which is a parameter of template struct Dependency. Depender can depend on the List of Dependees. I have some macros that can produce specializations of the Dependency template. Dots in the following code block stand for the possible macro expansion. I have a forward-declared MainDependee before dots, and MainDependee's definition after dots.
#include <type_traits>
template <typename T, typename = void>
struct IsCompleteType : std::false_type
{};
template <typename T>
struct IsCompleteType<T, std::enable_if_t<( sizeof( T ) > 0 )>> : std::true_type
{};
template<template<typename...> class List, typename Dependee>
struct DependencyInternal
{
using Type = std::conditional_t<IsCompleteType<Dependee>::value, Dependee, Dependee>;
};
template<typename... Ts> class StubList;
class MainDependee; // forward declaration
class MainDepender
{};
template<template<typename...> class List, typename Depender>
struct Dependency;
....... //here is the specialization
class MainDependee {};
int main()
{
Dependency<StubList, MainDepender> a;
}
When the dots are replaced with
template<template<typename... Us> class List>
struct Dependency<List, MainDepender>
{
using Type = typename DependencyInternal<List, MainDependee>::Type;
};
then in main I get IsCompleteType<MainDependee>::value == true.
But when the dots are replaced with
template<>
struct Dependency<StubList, MainDepender>
{
using Type = typename DependencyInternal<StubList, MainDependee>::Type;
};
then IsCompleteType<MainDependee>::value == false.
Please tell me, what rule describes the difference between these options?
Try the code yourself
An explicit (full) specialization is not itself a templated entity and all name lookup etc., as well as ODR, is done for it as if it wasn't a template. MainDependee is not complete at the point you wrote it and therefore IsCompleteType<MainDependee> is inherited from std::false_type at this point.
A partial specialization is itself a templated entity and will follow the rules for templates. In particular a definition will be instantiated from the partial specialization only when and where an instantiation is required. In this case the instantiation for Dependency<StubList, MainDepender> has its point of instantiation directly before main where MainDependee is complete. Only there Type is computed (because the right-hand side is dependent on the template parameter) and IsCompleteType<MainDependee> instantiated, so that it will be inherited from std::true_type.
Note that class template specializations are instantiated only once per translation unit. If you use Dependency<StubList, MainDepender>::value multiple times in a translation unit, it will always result in the same value, the one which would be correct at the first point from where instantiation is required.
Furthermore, if Dependency<StubList, MainDepender>::value or IsCompleteType<MainDependee> would have different values in different translation units because of different relative placement of MainDependee, your program will be IFNDR (ill-formed, no diagnostic required), effectively meaning it will have undefined behavior.
It is not possible to use a type trait like this to safely check whether a type is complete. For the same reason there is no such trait in the standard library.

How does enable_if help select specializations of a class template?

I have a basic grasp of SFINAE, and I think I understand many of the examples of how std::enable_if exploits it to select function template specializations, but I'm having a hard time wrapping my head around how it works for class templates.
The following example is from cppreference.com's explanation of std::enable_if:
template<class T, class Enable = void>
class A {}; // primary template
template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
}; // specialization for floating point types
I'm having trouble understanding how using std::enable_if in this way helps select the specialization. (I don't doubt that it does.)
When the compiler sees a declaration like A<float> specialized;, it will see two possible template instantiations that fit:
The "primary template" A<T, Enable> where T is the type float and Enable is the type void (because of the default value).
The specialization A<T, void> where T is the type float and void is the result of the expression with the enable_if.
Aren't those ambiguous? Both effectively result in A<T, void>, so why is the specialization chosen?
In a different case, like A<int> primary;, the options to the compiler seem to be:
The primary, A<T, Enable>, where T is the type int and Enable is the type void.
The specialization, A<T, ?>, where T is the type int and the ? represents where I'm completely lost. In this case, the enable_if condition is false, so it doesn't define type, which leaves you with A<int, typename >. Isn't that a syntax error? Even in the face of SFINAE?
From the reference on partial specialization of class templates:
When a class or variable (since C++14) template is instantiated, and there are partial specializations available, the compiler has to decide if the primary template is going to be used or one of its partial specializations.
If only one specialization matches the template arguments, that specialization is used
In this case, if the 2nd argument of the specialization is well-formed, it is chosen, precisely because it is a specialization, and not the primary template.
In case the 2nd template argument is not well-formed, then SFINAE kicks in. In particular:
When substituting the explicitly specified or deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error.
and
The following type errors are SFINAE errors:
attempting to use a member of a type, where
the type does not contain the specified member
How this is done, i.e. how exactly the compiler discards the specialization, instead of giving an error, is not specified; the compiler is just required to do the right thing.
Aren't those ambiguous? Both effectively result in A<T, void>, so why is the specialization chosen?
No, the specialization is more specialized than the primary template, because it requires the second parameter to be void (assuming the enable_if condition is true), while the primary template doesn't restrict it.
The specialization, A<T, ?>, where T is the type int and the ? represents where I'm completely lost. In this case, the enable_if condition is false, so it doesn't define type, which leaves you with A<int, typename >. Isn't that a syntax error? Even in the face of SFINAE?
Exactly, the second argument in the specialization turns out invalid. But this is a "soft" error, which is detected by SFINAE and makes the compiler discard the specialization. (I don't think a compiler would textually replace enable_if_t<...>::type with an empty string and then analyze A<int, typename >; more likely it would discard the specialization as soon as it notices the lack of ::type in enable_if.)

Check existence of operator()

I need a type trait HasCall to check validity of the folowing type instantiation for T:
template<class T> struct Caller: T
{
using T::operator();
};
Is there a way to do this in C++14?
Here is my attempt, but it doesn't work:
https://godbolt.org/z/vxgJCR
EDIT
I know about SFINAE and how it works. This problem is more dificult than just checking validity of some expression.
I want these asserts to pass:
struct A {void operator()(int) {}};
static_assert(HasCall<A>, "Check must work with any set of arguments.");
struct B {void operator()() {}};
static_assert(HasCall<B>, "Check must work with any set of arguments.");
struct C {template<typename... Args> void operator()(Args&&...) {}};
static_assert(HasCall<C>, "Templated operators must be detected correctly.");
struct D {};
static_assert(!HasCall<D>, "No operator() at all.");
static_assert(!HasCall<void(*)()>, "Class cannot inherit from function pointers.");
Checking validity of expression &T::operator() is not enough because it doesn't work with overloaded or template operator().
Please, check your solutions with these asserts.
This question is not a duplicate.
No, there is no way to do this.
All solutions require knowing the signature, either exact or compatible, of the call, or rely on no overloads.
In fact any overloaded or template call operator cannot be reliably detected even if you have a known signature, as implicit cast to function pointer permits declval tests to be spoofed.
You will have to find another way around your problem, or wait for reflection.
Try this:
template <typename T, typename = void> struct has_operator {
enum { value = 0 };
};
// thanks super for great suggestion!
template <typename T> struct has_operator<T, std::void_t<decltype(std::declval<T>()())>> {
enum { value = 1 };
};
template<class T, typename = std::enable_if<has_operator<T>::value>> struct Caller: T
{
using T::operator();
};
This works by principle of SFINAE - some errors in template instantation instead of failing whole compilation will just make compiler ignore given instantation. So first we define has_operator with value = 0, which is our "default value". Then we make specialization of the template for type T. Now we want this specialization to be picked only, when T::operator() exists. So we add second template argument and set it's default value to decltype(&T::operator()) and value = 1. We don't care for real type here, what we care is that if T::operator() exists, this will compile just fine. And compiler will select this specialization for this T. When it doesn't exist - compiler will ignore this specialization and select "default" has_operator, which has value = 0.
So now we have struct has_operator, which - when used like this: has_operator<T>::value will produce constant value 0, when T don't have operator () (with any arguments, mind you) and value 1, when has. You can use it with std::enable_if (which btw works in pretty much the same way).
The list of things, that can be applied with this technique is rather long - pretty much anything, that can make or break compilation can be used.

Should the following code compile according to C++ standard?

#include <type_traits>
template <typename T>
struct C;
template<typename T1, typename T2>
using first = T1;
template <typename T>
struct C<first<T, std::enable_if_t<std::is_same<T, int>::value>>>
{
};
int main ()
{
}
Results of compilation by different compilers:
MSVC:
error C2753: 'C': partial specialization cannot match argument list for primary template
gcc-4.9:
error: partial specialization 'C' does not specialize any template arguments
clang all versions:
error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list
gcc-5+:
successfully compiles
And additionaly I want to point out that trivial specialization like:
template<typename T>
struct C<T>
{
};
successfully fails to be compiled by gcc. So it seems like it figures out that specialization in my original example is non-trivial. So my question is - is pattern like this explicitly forbidden by C++ standard or not?
The crucial paragraph is [temp.class.spec]/(8.2), which requires the partial specialization to be more specialized than the primary template. What Clang actually complains about is the argument list being identical to the primary template's: this has been removed from [temp.class.spec]/(8.3) by issue 2033 (which stated that the requirement was redundant) fairly recently, so hasn't been implemented in Clang yet. However, it apparently has been implemented in GCC, given that it accepts your snippet; it even compiles the following, perhaps for the same reason it compiles your code (it also only works from version 5 onwards):
template <typename T>
void f( C<T> ) {}
template <typename T>
void f( C<first<T, std::enable_if_t<std::is_same<T, int>::value>>> ) {}
I.e. it acknowledges that the declarations are distinct, so must have implemented some resolution of issue 1980. It does not find that the second overload is more specialized (see the Wandbox link), however, which is inconsistent, because it should've diagnosed your code according to the aforementioned constraint in (8.2).
Arguably, the current wording makes your example's partial ordering work as desired†: [temp.deduct.type]/1 mentions that in deduction from types,
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values […] that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
Now via [temp.alias]/3, this would mean that during the partial ordering step in which the partial specialization's function template is the parameter template, the substitution into is_same yields false (since common library implementations just use a partial specialization that must fail), and enable_if fails.‡ But this semantics is not satisfying in the general case, because we could construct a condition that generally succeeds, so a unique synthesized type meets it, and deduction succeeds both ways.
Presumably, the simplest and most robust solution is to ignore discarded arguments during partial ordering (making your example ill-formed). One can also orientate oneself towards implementations' behaviors in this case (analogous to issue 1157):
template <typename...> struct C {};
template <typename T>
void f( C<T, int> ) = delete;
template <typename T>
void f( C<T, std::enable_if_t<sizeof(T) == sizeof(int), int>> ) {}
int main() {f<int>({});}
Both Clang and GCC diagnose this as calling the deleted function, i.e. agree that the first overload is more specialized than the other. The critical property of #2 seems to be that the second template argument is dependent yet T appears solely in non-deduced contexts (if we change int to T in #1, nothing changes). So we could use the existence of discarded (and dependent?) template arguments as tie-breakers: this way we don't have to reason about the nature of synthesized values, which is the status quo, and also get reasonable behavior in your case, which would be well-formed.
† #T.C. mentioned that the templates generated through [temp.class.order] would currently be interpreted as one multiply declared entity—again, see issue 1980. That's not directly relevant to the standardese in this case, because the wording never mentions that these function templates are declared, let alone in the same program; it just specifies them and then falls back to the procedure for function templates.
‡ It isn't entirely clear with what depth implementations are required to perform this analysis. Issue 1157 demonstrates what level of detail is required to "correctly" determine whether a template's domain is a proper subset of the other's. It's neither practical nor reasonable to implement partial ordering to be this sophisticated. However, the footnoted section just goes to show that this topic isn't necessarily underspecified, but defective.
I think you could simplify your code - this has nothing to do with type_traits. You'll get the same results with following one:
template <typename T>
struct C;
template<typename T>
using first = T;
template <typename T>
struct C<first<T>> // OK only in 5.1
{
};
int main ()
{
}
Check in online compiler (compiles under 5.1 but not with 5.2 or 4.9 so it's probably a bug) - https://godbolt.org/g/iVCbdm
I think that int GCC 5 they moved around template functionality and it's even possible to create two specializations of the same type. It will compile until you try to use it.
template <typename T>
struct C;
template<typename T1, typename T2>
using first = T1;
template<typename T1, typename T2>
using second = T2;
template <typename T>
struct C<first<T, T>> // OK on 5.1+
{
};
template <typename T>
struct C<second<T, T>> // OK on 5.1+
{
};
int main ()
{
C<first<int, int>> dummy; // error: ambiguous template instantiation for 'struct C<int>'
}
https://godbolt.org/g/6oNGDP
It might be somehow related to added support for C++14 variable templates. https://isocpp.org/files/papers/N3651.pdf

What exactly happens in this template metaprogramming snippet?

template <typename T, typename = void>
struct IsIterable : std::false_type {};
template <typename T>
struct IsIterable<
T,
decltype(
std::begin(std::declval<std::add_lvalue_reference_t<T>>()),
std::end(std::declval<std::add_lvalue_reference_t<T>>()),
void()
)
> : std::true_type {};
I thought I understood this.
The first IsIterable is a class template with a default template argument for the second parameter. It obviously is applicable for every type.
The second part is a partial template specialization of IsIterable. Thanks to SFINAE, it is only chosen when T has the member functions begin() and end().
Here is my first question I just came up with actually: the base template has a default argument, so this means it still has two template parameters. The partial specialization only has one template parameter. So does this mean that "one template parameter" always gets choosen before "two template parameters, one default"?
Also, do I understand it correctly that the first "base" template inherits from false_type, and the partial template specialization "adds" another inheritance level? (so does the partial specializations inheritance hierarchy look like this: false_type > true_type > IsIterable, where the definitions of false_type are hidden by true_type?)
Now onto my actual question. Why does the decltype expression have to evaluate to void? I thought that it wouldn't matter and I could write
template <typename T>
struct IsIterable<
T,
decltype(
std::begin(std::declval<std::add_lvalue_reference_t<T>>()),
std::end(std::declval<std::add_lvalue_reference_t<T>>()),
bool() // **** change here ****
)
> : std::true_type {};
as well. But this makes it so that the value of IsIterable is always false! Why is, when I change the partial specialization from void to bool, always the first template chosen?
So does this mean that "one template parameter" always gets choosen before "two template parameters, one default"?
No. A partial specialization is used when an instantiation matches its template argument list, and when it is "more specialized" than any other partial specializations that match (which is not relevant here, because there are no other partial specializations declared).
When you instantiate the template as IsIterable<Foo> the default template argument is used, so the instantiation is IsIterable<Foo, void>. If Foo has begin() and end() members then IsIterable<Foo, void> matches the partial specialization, which is more specialized than the primary template. When Foo doesn't have begin() and end() members the partial specialization is not usable, because the expression decltype(std::declval<T>().begin(), std::declval<T>().end(), void()) causes a subsitution failure when Foo is substituted in place of T.
Also, do I understand it correctly that the first "base" template inherits from false_type, and the partial template specialization "adds" another inheritance level?
No. There is no implicit inheritance relationship between a primary template and a specialization. (If you stop calling it a "base" template and call it by its proper name, the primary template, maybe you won't think there is any kind of "base" class relationship.)
When a partial specialization (or an explicit specialization) is used it is used instead of the primary template, not in addition to it.
Why does the decltype expression have to evaluate to void?
Because that's the type of the default template argument on the primary template. You are not supposed to provide an argument for the second template parameter, it's supposed to use the default, and that is void.
Why is, when I change the partial specialization from void to bool, always the first template chosen?
Because when you write IsIterable<Foo> that uses the default template argument, so is equivalent to IsIterable<Foo, void> which can never match a partial specialization of the form IsIterable<T, bool> because void is not the same type as bool!
If you made the partial specialization use bool() instead then you would have to write IsIterable<Foo, bool> for the partial specialization to match. You could do that ... but it's not how the trait is designed to be used. Alternatively you could change the default template argument on the primary template to be bool as well, but void is the idiomatic choice, since the specific type doesn't matter, all that matters is the default matches the specialization. You're meant to provide only one template argument and let the default be used for the second. And the partial specialization can only match if its second template argument is the same as the default template argument.
The second part is a partial template specialization of IsIterable. Thanks to SFINAE, it is only chosen when T has the member functions begin() and end().
That's not all there is. The expression t.begin(), t.end(), void() for a t of type T must also have a valid type and that must be void for the specialization to be selected. (That's what the decltype achieves.)
Here is my first question I just came up with actually: the base template has a default argument, so this means it still has two template parameters. The partial specialization only has one template parameter. So does this mean that “one template parameter” always gets choosen before “two template parameters, one default”?
Every valid instantiation of the specialization is also a valid instantiation of the base case (it's an implication) so the specialization (if viable) is a better match.
Also, do I understand it correctly that the first “base” template inherits from false_type, and the partial template specialization “adds” another inheritance level? (so does the partial specializations inheritance hierarchy look like this: false_type > true_type > IsIterable, where the definitions of false_type are hidden by true_type?)
No, they are completely unrelated types. A specialization of a class template does not inherit implicitly from the general case.
If you want to see this live, try adding a large non-static data member (eg int[100]) to the general case and then compare the sizeof the instantiated types. If the special case is smaller, it cannot possibly be derived from the general case.
Now onto my actual question. Why does the decltype expression have to evaluate to void?
It doesn't have to but in order to make this work, you'll also have to change the default for the base case from void to bool or whatever. Be aware, however, that an overloaded operator, could cause you strange surprises if you pick anything but void.
The technique was nicely explained in a talk Walter Brown gave at CppCon 2014. If you have two hours sparse, I strongly recommend you watch the recording of his talk:
Walter E. Brown, Modern Template Metaprogramming: A Compendium, Part I. CppCon 2014.
Walter E. Brown, Modern Template Metaprogramming: A Compendium, Part II. CppCon 2014