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
Related
I would like to define a derived class that has a template template type for a templated base class. Is this possible, and if so what is the syntax? I have tried a lot of things without success. Please consider this code:
#include <iostream>
template <typename U>
struct Wrapper {};
template <typename U>
struct Base {};
// This Works, define a template template type class
template<class>
struct Other;
template<template<typename T> typename W, typename T>
struct Other<W<T>> {};
// How do I define this?
template<template<typename T> typename W, typename T>
struct Derived : public Base<W<T>> {};
using namespace std;
int main()
{
Wrapper<float> w;
Base<int> b;
Other<Wrapper<int>> o;
Derived<Wrapper<int>> d; // fails to compile
return 0;
}
Why: I have existing/working code that has Derived<U> where U is a wrapped class W<T>. Specifying Derived<W<T>> in the definition using a template template type was unnecessary; Derived<U> is sufficient. Both W and T can be many different concrete types, but T will always have a statically available method that I now need access to from within Derived scope.
I am currently and woefully limited to C++11.
Often, things get much simpler if you realize that, for most regards, an instantiation of a class template is just a type like any other. And confusing types with templates is also the source of your error.
Wrapper<int> is a type. The first argument for the Derived template is a template, not a type.
Other is a template that has a single type argument. It has a partial specialization for the case that this type is an instantiation of some template W.
Derived is a template that has two arguments. The first is a template argument, the second is a type. An instantiation is, for example: Derived<W,int>.
If you want to make Derived a template with a single type argument, with a partial specialization for the case that this type is an instantiation of Wrapper, then you need to do the same as you did for Other: A base template with a single type argument and a specialization, eg:
template<class> struct Derived;
template<template<typename T> typename W, typename T>
struct Derived<W<T>> : public Base<W<T>> {};
The second line is the partial specialization. It does not change the fact that Derived has one type argument. It only makes Derived<W<T>> match this specialization no matter what T is.
I am attempting to figure out how to special a template class based upon the result of a template. For example, suppose I have the following basic template class:
enum class BasicType
{
UNKNOWN,
PRIMITIVE,
OBJECT
};
template <typename T>
struct traits
{
static constexpr BasicType type = BasicType::UNKNOWN;
};
I want to be able to create a specialization of traits, for example, for any types where std::is_integral_v is true. And then another specialization where another traits is true.
I tried something like this
template <typename T>
struct traits<std::enable_if_t<std::is_integral_v<T>, T>>
{
static constexpr BasicType type = BasicType::PRIMITIVE;
};
But this doesn't work.
You're close, but the problem with using enable_if like this is that it prevents type deduction. Basically, the compiler is unable to ever match your template specialization. What you can do, though, is to move the enable_if part to a second template parameter next to your T so it can be deduced:
template <typename T, typename = void>
struct traits
{
static constexpr BasicType type = BasicType::UNKNOWN;
};
template <typename T>
struct traits<T, std::enable_if_t<std::is_integral_v<T>>>
{
static constexpr BasicType type = BasicType::PRIMITIVE;
};
This works because T is no longer hidden behind another template. The compiler is still unable to deduce T from std::enable_if_t<std::is_integral_v<T>>, but because you also specified T direclty here: struct traits<T, ...>, the compiler can deduce T this way, which is enough to match your specialization.
example
I am new to template metaprogramming and I was watching the type traits talk part II by Jody Hagins. I wanted to replicate a function overload resolution example to detect whether a given type is constant using the following:
namespace detail {
template <typename T>
std::true_type is_const(T const);
template <typename T>
std::false_type is_const(T);
} // namespace detail
template <typename T>
using is_constant = decltype(detail::is_const(std::declval<T>()));
static_assert(is_constant<int const>::value);
The above static assertion produces a compiler error saying call to is_const is ambiguous. If I use a TypeTag to enclose T in my template declarations, things work as expected:
template <typename T>
struct TypeTag {};
namespace detail {
template <typename T>
std::true_type is_const(TypeTag<T const>);
template <typename T>
std::false_type is_const(TypeTag<T>);
} // namespace detail
template <typename T>
using is_constant = decltype(detail::is_const(std::declval<TypeTag<T>>()));
static_assert(is_constant<int const>::value);
I am confused as to why the first declarations without TypeTag encapsulation are ambiguous. My guess is it has something to do with declval return type being T for cv-qualified types but then I do not understand how the second case works.
Is it because in the first case declval<int const> has return type int but in the second case declval<TypeTag<int const>> has return type TypeTag<int const> so the compiler picks the first template specialization where T is replaced with int and the template invocation looks like this:
<>
std::true_type is_const<TypeTag<int const>>;
If my guess is correct, is there a general practice to use tag dispatching with a TypeTag (empty struct template) to guard against cv-qualified types?
The reason the 1st example does not work is that top-level const in a function argument is ignored, so is_const(T) and is_const(T const) are the same function signatures. If const is not top-level, function signatures are different, e.g. is_const(T*) and is_const(T* const) are different.
In your 2nd example, is_const(TypeTag<T>) and is_const(TypeTag<T const>) are different because TypeTag<T> and TypeTag<T const> are unrelated types.
However, I don't think that your use of TypeTag qualifies as "tag dispatch".
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.
While writing a small template metaprogramming library for personal use, I came across an interesting problem.
Since I was reusing a few partial specializations for some metafunctions, I decided I would put them under a common template class and use tags along with nested partial specialization to provide the differences in behaviour.
The problem is I am getting nonsensical (to me) results. Here is a minimal example that showcases what I am trying to do:
#include <iostream>
#include <cxxabi.h>
#include <typeinfo>
template <typename T>
const char * type_name()
{
return abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);
}
template <typename... Args>
struct vargs {};
namespace details
{
template <typename K>
struct outer
{
template <typename Arg>
struct inner
{
using result = Arg;
};
};
}
struct tag {};
namespace details
{
template <>
template <typename Arg, typename... Args>
struct outer<tag>::inner<vargs<Arg, Args...>>
{
using result = typename outer<tag>::inner<Arg>::result;
};
}
template <typename T>
using test_t = typename details::outer<tag>::inner<T>::result;
int main()
{
using t = test_t<vargs<char, int>>;
std::cout << type_name<t>() << '\n';
return 0;
}
I am getting vargs<char, int> as output when using the 5.1.0 version of gcc and tag when using the 3.6.0 version of clang. My intention was for the above piece of code to print char so I am pretty baffled by these results.
Is the above piece of code legal or does it exhibit undefined behavior?
If it's legal what is the expected behavior according to the standard?
Your code is correct; out-of-class implicitly instantiated class template member class template partial specializations are intended to be allowed by the Standard, as long as they are defined early enough.
First, let's try for a minimal example - noting by the way that there's nothing here that requires C++11:
template<class T> struct A {
template<class T2> struct B { };
};
// implicitly instantiated class template member class template partial specialization
template<> template<class T2>
struct A<short>::B<T2*> { };
A<short>::B<int*> absip; // uses partial specialization?
As noted elsewhere MSVC and ICC use the partial specialization as expected; clang selects the partial specialization but messes up its type parameters, aliasing T2 to short instead of int; and gcc ignores the partial specialization entirely.
Why out-of-class implicitly instantiated class template member class template partial specialization is allowed
Put simply, none of the language that permits other forms of class template member class template definitions excludes out-of-class implicitly instantiated class template member class template partial specialization. In [temp.mem], we have:
1 - A template can be declared within a class or class template; such a template is called a member template. A
member template can be defined within or outside its class definition or class template definition. [...]
A class template partial specialization is a template declaration ([temp.class.spec]/1). In the same paragraph, there is an example of out-of-class nonspecialized class template member class template partial specialization ([temp.class.spec]/5):
template<class T> struct A {
struct C {
template<class T2> struct B { };
};
};
// partial specialization of A<T>::C::B<T2>
template<class T> template<class T2>
struct A<T>::C::B<T2*> { };
A<short>::C::B<int*> absip; // uses partial specialization
There is nothing here to indicate that the enclosing scope cannot be an implicit specialization of the enclosing class template.
Similarly, there are examples of in-class class template member class template partial specialization and out-of-class implicitly instantiated class template member class template full specialization ([temp.class.spec.mfunc]/2):
template<class T> struct A {
template<class T2> struct B {}; // #1
template<class T2> struct B<T2*> {}; // #2
};
template<> template<class T2> struct A<short>::B {}; // #3
A<char>::B<int*> abcip; // uses #2
A<short>::B<int*> absip; // uses #3
A<char>::B<int> abci; // uses #1
(clang (as of 3.7.0-svn235195) gets the second example wrong; it selects #2 instead of #3 for absip.)
While this does not explicitly mention out-of-class implicitly instantiated class template member class template partial specialization, it does not exclude it either; the reason it isn't here is that it's irrelevant for the particular point being made, which is about which primary template or partial template specializations are considered for a particular specialization.
Per [temp.class.spec]:
6 - [...] when the primary
template name is used, any previously-declared partial specializations of the primary template are also
considered.
In the above minimal example, A<short>::B<T2*> is a partial specialization of the primary template A<short>::B and so should be considered.
Why it might not be allowed
In other discussion we've seen mention that implicit instantiation (of the enclosing class template) could result in implicit instantiation of the definition of the primary template specialization to take place, resulting in an ill-formed program NDR i.e. UB; [templ.expl.spec]:
6 - If a template, a member template or a member of a class template is explicitly specialized then that specialization
shall be declared before the first use of that specialization that would cause an implicit instantiation
to take place, in every translation unit in which such a use occurs; no diagnostic is required. [...]
However, here the class template member class template is not used before it is instantiated.
What other people think
In DR1755 (active), the example given is:
template<typename A> struct X { template<typename B> struct Y; };
template struct X<int>;
template<typename A> template<typename B> struct X<A>::Y<B*> { int n; };
int k = X<int>::Y<int*>().n;
This is considered problematic only from the point of view of the existence of the second line instantiating the enclosing class. There was no suggestion from the submitter (Richard Smith) or from CWG that this might be invalid even in the absence of the second line.
In n4090, the example given is:
template<class T> struct A {
template<class U> struct B {int i; }; // #0
template<> struct B<float**> {int i2; }; // #1
// ...
};
// ...
template<> template<class U> // #6
struct A<char>::B<U*>{ int m; };
// ...
int a2 = A<char>::B<float**>{}.m; // Use #6 Not #1
Here the question raised is of precedence between an in-class class template member class template full specialization and an out-of-class class template instantiation member class template partial specialization; there is no suggestion that #6 would not be considered at all.