For educative reasons, I was playing around with the SFINAE behavior of C++ and building my own version of std::enable_if in a rather simplified form. I noticed different behavior when using slightly different implementation details though:
Implementation as incomplete type:
template <bool C, typename> struct enable_if; // incomplete type
template <typename T> struct enable_if<true, T> { typedef T type; };
Implementation as empty type:
template <bool C, typename> struct enable_if {}; // empty type
template <typename T> struct enable_if<true, T> { typedef T type; };
On g++ (4.8.1 and 4.3.2) both versions compile and behave the same way. MSVC 2008 seems to only accept the definition as an empty type.
Are both definitions valid C++, and should they be equivalent in behavior?
From the standard, § 14.8.2:
Type deduction may fail for the following reasons:
[...]
Attempting to use a type in a nested-name-specifier of a qualified-id
when that type does not contain the specified member, or the specified
member is not a type where a type is required [...]
Both cases are handled in the same sentence, so from my understanding it really should not make a difference - both implementations should be equivalent.
Related
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.
How can I check at compile-time whether or not an arbitrary type can be used with std::pointer_traits? I had hoped a simple SFINAE solution might work:
template <typename T, typename = void>
struct pointer_traits_ready : std::false_type {};
template <typename T>
struct pointer_traits_ready<
T,
std::void_t<typename std::pointer_traits<T>::element_type>
> : std::true_type {};
static_assert(!pointer_traits_ready<int>::value,"");
...but this invokes a static assert from within the standard library (ptr_traits.h). Obviously std::is_pointer doesn't accommodate smart pointers.
You can't. From [pointer.traits.types]:
using element_type = see below ;
Type: Ptr::element_type if the qualified-id Ptr::element_type is valid and denotes a type (14.8.2); otherwise, T if Ptr is a class template instantiation of the form SomePointer<T, Args>, where Args is
zero or more type arguments; otherwise, the specialization is ill-formed.
In order to be SFINAE-friendly, we would need pointer_traits<Foo> to simply lack a type alias named element_type. The problem is, element_type is specified as being ill-formed - not absent. So you simply cannot use pointer_traits as a detector for whether or not something can be used as a pointer type.
Even if you wrote your own type that was a SFINAE-friendly version of that specification, you wouldn't be able to catch user specializations of pointer_traits for their own type. Sad panda.
An allocator can optionally have nested types like pointer, const_pointer. But one can always use these interface with std::allocator_traits<Allocator>, which would provide a default version of these types if they are absent in Allocator.
How is std::allocator_traits implemented? How can a template choose a default version of nested type when it's absent?
The solution is to refer to the type T::pointer in a context where it does not cause an error if it is not a valid type, instead it causes template argument deduction to fail. The general form of this is known as SFINAE, which stands for "Substitution Failure Is Not An Error". For a explanation of how it works see my SFINAE Functionality Is Not Arcane Esoterica presentation.
There are various techniques, often involving overloaded function templates, but my current favourite uses the void_t idiom to select a partial specialization of a class template:
template<typename T>
using void_t = void;
template<typename T, typename = void>
struct get_pointer
{
using type = typename T::value_type*;
};
template<typename T>
struct get_pointer<T, void_t<typename T::pointer>>
{
using type = typename T::pointer;
};
Now given an allocator type A you can use typename get_pointer<A>::type to refer to A::pointer if that exists, otherwise A::value_type*
The code above works because when A::pointer is a valid type the partial specialization matches and is more specialized than the primary template, and so gets used. When A::pointer is not a valid type the partial specialization is ill-formed, so the primary template is used.
Template template typename?
When using template template syntax as in template <template <typename> class T>, it is required to use the keyword class, as using typename gives an error along the lines of:
error: template template parameter requires 'class' after the parameter list
Everywhere else the keywords typename and class are interchangeable in the basic case of declaring a template parameter.
You could argue that the requirement when using template template is a hint that you are expected to pass a class type, but this is not always the case (especially not after C++11 introduced templated type aliases).
template <template <typename> class T> // 'class' keyword required.
struct Foo {
using type = T<int>;
};
template <typename T>
using type = T (*)();
using func_ptr_t = Foo<type>::type;
What is the reasoning behind this?
Is there any specific reason as to why typename is not allowed in template template declarations?
Does the C++ standard say anything about this?
Short answer: because the Standard says so.
Longer answer: prior to Standardization, C++ templates required the class keyword for all template parameters. However, to stress the fact that templates could also be of non-class (i.e. builtin) type, an alternative keyword typename was introduced. However, in C++98, template-template parameters could only be of class-type, and this was the reason that the typename keyword was not added in that context.
Enter C++11 and its new feature template aliases, that now also introduced non-class templates, and hence non-class template-template parameters:
template<typename T> struct A {};
template<typename T> using B = int;
template<template<typename> class X> struct C;
C<A> ca; // ok
C<B> cb; // ok, not a class template
template<template<typename> typename X> struct D; // error, cannot use typename here
The above example was taken from the current C++1z proposal N4051 titled Allow typename in a template template parameter, and proposes to allow precisely that.
Clang 3.5 SVN now supports this with the -std=c++1z flag.
I'm looking for the rational behind this restriction [...]
Before C++11 was introduced, the only templates you could pass to a template template parameter were class templates.
That's why the use of the keyword class was enforced.
Additionally, the keyword typename implies that the template parameter is a substitution for an arbitrary type and not a template, so using typename in that context would just blur the line between the names of types and (class) templates.
That's comprehensible.
Nowadays, such arguments can be the names of class templates or alias templates, and since those aren't even remotely connected, the enforcement of the keyword class is more or less obsolete. The proposal N4051 opts to change this with C++1Z.
Sorry for the funny title.
Prior to C++0x, there are restrictions in the use of function-local structs (“local types”) as template arguments. My question is essentially if similar restrictions apply to anonymous structs. Specifically, in the context of a trait class:
template <typename T>
struct trait;
template <>
struct trait<int> {
typedef int type;
};
template <typename T>
struct trait<std::basic_string<T> > {
typedef struct {
T value;
} type;
};
trait<std::string>::type foo; // Is this valid?
template <typename T>
void f() { }
f<trait<std::string>::type> >(); // Is this?
template <typename T>
void g() { f<typename trait<T>::type>(); }
g<std::string>(); // And this?
Is this valid and reliable? It compiles in recent versions of GCC and LLVM but I’m still insecure whether this is strictly valid, and whether it’s understood by VC++ and ICC.
For reference, the quote from the linked question in 14.3.1/2:
A local type, a type with no linkage,
an unnamed type or a type compounded
from any of these types shall not be
used as a template argument for a
template type parameter.
My interpretation is that the typedef struct is creating an alias to an unnamed type and that it thus can't be used as a template type parameter. Further note that additionally in C typedef struct {} Foo; is treated rather differently from struct Foo {}; giving precedent that the two forms are not equivalent (although admittedly that difference doesn't appear in C++).
Thus it would appear your first example works (since it's not using the unnamed type as a template type parameter), while the second and third examples would be technically invalid (since they do use it as a template type parameter).
Finally in closing I have to ask, is there a reason you can't name the struct instead of typedefing it?
EDIT: From 7.1.3/1:
...A typedef-name is thus a synonym for
another type. A typedef-name does not
introduce a new type the way a class
declaration (9.1) or enum declaration
does...
This strongly implies that using typedef in such a way does not introduce a type suitable for use as a template type-parameter.
In the upcoming standard that restriction is removed from the language. The standard says in
14.3.1 [temp.arg.type] /1
A template-argument for a template-parameter which is a type shall be a type-id.
And a typedef is a valid type-id. As a matter of fact the next paragraph contains such an example:
14.3.1 [temp.arg.type] /2
template <class T> class X { };
template <class T> void f(T t) { }
void f() {
typedef struct { } B;
B b;
X<B> x3;
f(b);
}
(Where I have trimmed most of the other examples) The example shows that an unnamed type can be used as a class template argument both in class templates and function templates.
A typedef declaration that defines an anonymous class and a typedef-name for that class, the typedef-name is the name of the class for linkage purposes. It is therefore legal to use that class as a template parameter if it meets the other criteria.
See 7.1.3p5 of the C++03 standard
If the typedef declaration defines an
unnamed class (or enum), the first
typedef-name declared by the decla-ration to be that class type (or enum
type) is used to denote the class type
(or enum type) for linkage purposes
only (3.5). [Example:
typedef struct { } *ps, S; // S is the class name for linkage purposes
This is 7.1.3p9 in the C++0x FDIS.
FWIW, this code compiles OK with MSVC2010 (modulo typos).
Well, that is equivalent to
template <typename T>
struct trait<std::basic_string<T> > {
struct type {
T value;
};
};
which is completely legitimate.