I may write as
template< class T0> struct Last0
{
using type = decltype(T0{}); // OK compiles. `type = T0`
};
template< class T0, class T1> struct Last1
{
using type = decltype(T0{}, T1{}); // OK, compiles. `type = T1`
};
template< class T0, class T1, class T2> struct Last3{
using type = decltype(T0{}, T1{}, T2{}); // Ok, compiles. `type = T2`
};
But, when I use variadic templates, it's not compiled:
template< class ... T> struct Last{
using type = decltype(T{} ... ); //<--- Error !!!
};
What's problem?
There is a taxative list of language constructs where pack expansion can happen (C++11, 14.5.3ยง4). With the exception of sizeof..., it's always in constructs where the comma , is a grammatical separator of a list, and not an operator. An expression cannot be a pack expansion.
To get the last type in a pack, you can do this:
template <class Head, class... Tail>
struct Last {
typedef typename Last<Tail...>::Type Type;
};
template <class Head>
struct Last<Head> {
typedef Head Type;
};
You can only apply decltype to an expression, not to a pack. Packs are very special and basically always need to be expanded. You essentially have the same problem as not being able to store packs directly: using type = T... isn't allowed, either.
The standard solution is to store packs inside some "container template", typically tuple:
using T = std::tuple<decltype(T{})...>;
Related
I have a class template that has members of some type. This type is determined based on the type that is provided when instantiating the template. It uses a default (double in the example below) unless an override is provided by that class. Classes used as template types may provide this override type (here "Two" provides the override type "int"). If a class provides the override, the override should only be used if the class also sets the UseOverride flag. If flag is absent or false, default "double" should be used.
Problem is that if the template type does not provide the "type", then compiler gives error in below code. I suspect I need to use SFINAE here, but haven't been able to figure out a suitable approach for it, even after puzzling and browsing related questions for a good part of the afternoon.
How to define the EventType template so that it works as intended? I want to keep the EventType<T> syntax.
#include <iostream>
struct One {
//This type is ignored, and double is used, because UseOverride = true is not specified:
using type = short;
};
struct Two {
static constexpr bool UseOverride = true;
using type = int;
};
struct Three {
static constexpr bool UseOverride = false;
//I don't want the compiler to complain that "type" is not available here (because it should default to double anyhow since
//the class instructs not to use the override). But compile does generate error.
//How to avoid this?
};
template <typename T, typename = void>
struct overrideInfoProvided : std::false_type {};
template <typename T>
struct overrideInfoProvided<T, decltype((void)T::UseOverride, void())> : std::true_type {};
template <typename T>
constexpr bool Override()
{
if constexpr (overrideInfoProvided<T>::value)
{
return T::UseOverride;
}
return false;
}
template<class T>
using EventType = typename std::conditional_t<Override<T>(), typename T::type, double>;
template <class T>
struct Test
{
typename EventType<T> member;
Test()
{
std::cout << member << std::endl;
}
};
int main()
{
Test<One>();
Test<Two>();
//Gives error:
//Test<Three>();// `type': is not a member of any direct or indirect base class of `three';
}
I don't want the compiler to complain that "type" is not available here (because it should default to double anyhow since the class instructs not to use the override). But compiler does generate error. How to avoid this?
Just defer the access to ::type with the below type_identity helper:
template <typename T>
struct type_identity { using type = T; };
template <typename T>
using EventType = typename std::conditional_t<Override<T>()
, T
, type_identity<double>>::type;
// ~~~~~~~~~~~~^
DEMO
You are on the right track, but you don't need to have separate checks for the existence of useOverride, and type. Instead, you can do both of the checks in the same sfinae class:
template <typename T, typename = void, typename = void>
struct EventType_T {
using t = double; // default if useOverride or type doesn't exist
};
template <typename T>
struct EventType_T<T, std::void_t<decltype(T::UseOverride)>,
std::void_t<typename T::type>> {
// choose type if useOverride is true, and double otherwise
using t = std::conditional_t<T::UseOverride, typename T::type, double>;
};
template <typename T>
using EventType = typename EventType_T<T>::t;
Here's a demo. This allows you to still use the EventType<T> syntax as before.
Note, the t member instead of type is unconventional, but since we are already testing for a type member in T, this might make it clearer what's going on. I would recommend using type once yo understand how the solution works.
I'm trying to write a C++ metafunction that returns to me the first non-empty sub-type for the provided template parameters.
For example:
struct I { using subtype = int; };
struct D { using subtype = double; };
struct E { using subtype = empty ; };
I'm trying to achieve:
static_assert(std::is_same<int, first_non_empty_subtype<E,E,I>>::value, "the first non-empty subtype should be 'int'");
static_assert(std::is_same<double, first_non_empty_subtype<E,D,I>>::value, "the first non-empty subtype should be 'double'");
static_assert(std::is_same<empty, first_non_empty_subtype<E,E,E>>::value, "since all subtypes are empty, the result is empty");
My initial thoughts are to use std::conditional_t with template recursion:
template <typename T, typename ...Ts>
using first_non_empty_subtype = std::conditional_t<
!std::is_empty<typename T::subtype>::value,
typename T::subtype,
first_non_empty_subtype<Ts...>>::type
However, I'm not all familiar with implementing template recursion for type aliases.
Can someone help point me in the right direction for solving this problem?
Thanks!
I propose something as follows
// ground case: no more types, so empty
template <typename ...>
struct fnes_helper
{ using type = empty; };
// the first type is T and isn't empy; so T
template <typename T, typename ... Ts>
struct fnes_helper<T, Ts...>
{ using type = T; };
// the first type is empty; so recursion
template <typename ... Ts>
struct fnes_helper<empty, Ts...> : public fnes_helper<Ts...>
{ };
template <typename ... Ts>
using first_non_empty_subtype
= typename fnes_helper<typename Ts::subtype...>::type;
Observe that the fnes_helper more specialized version is the one with the empty type in first position, so is the version used in that case.
Follows the other specialization, the one with a generic T type in first position and finally we have the main version that is selected in the other cases, so where the list of types is empty.
Also remember to add a {} or a ::value after std::is_same in the static_assert() tests
static_assert(std::is_same<int, first_non_empty_subtype<E,E,I>>{},
"the first non-empty subtype should be 'int'");
static_assert(std::is_same<double, first_non_empty_subtype<E,D,I>>{},
"the first non-empty subtype should be 'double'");
static_assert(std::is_same<empty, first_non_empty_subtype<E,E,E>>{},
"since all subtypes are empty, the result is empty");
EDIT: this other question of mine focuses on a reduced version of this problem, possibly easier to understand.
I wrote a small snippet that reproduces the behaviour of std::experimental::is_detected (here). My implementation is basically taken from cppreference but I got rid of the Default template parameter.
My question is: in the following snippet, why does has_type (the condition to be checked) have to be a using declaration and cannot be, e.g. a struct (in which case is_detected returns a wrong result)?
/***** is_detected definition *****/
template<typename...Args>
using void_t = void;
template<typename Void, template<class...> class Op, typename ...Args>
struct Detector {
static constexpr bool value = false;
};
template<template<class ...> class Op, typename ...Args>
struct Detector<void_t<Op<Args...>>, Op, Args...> {
static constexpr bool value = true;
};
template<template<class...> class Op, typename...Args>
using is_detected_t = Detector<void, Op, Args...>;
/****************************/
/***** is_detected test *****/
// two dummy types on which to test a condition
struct Yes { using type = void; };
struct No { };
// the condition to test
template<typename T>
using has_type = typename T::type;
// struct has_type { using type = typename T::type; }; // does not work as intended!
int main() {
static_assert(is_detected_t<has_type, Yes>::value, "");
static_assert(!is_detected_t<has_type, No>::value, "");
return 0;
}
It might help to look at how has_type is actually used by the detector:
template<template<class ...> class Op, typename ...Args>
struct Detector<void_t< Op<Args...>>, Op, Args...> {
// ^^ ^^
// has_type Yes/No
static constexpr bool value = true;
};
For this specialization to match the compiler must make sure that Op<Args...>, when replacing the parameters (Op and Args...) with the actual arguments (has_type and Yes/No), must name a type (since that's what the template void_t requires as first template argument).
Since has_type is not a type, but rather an alias of some type, it must look whether whatever is aliased names a type.
For Yes this will be Yes::type, which again is an alias of void. void is a type, so everything is fine, the specialization matches, value is true.
For No this will be No::type, which does not exist (No has no member type after all). Thus, the substitution fails (but this is not an error, SFINAE), the specialization cannot be used. Thus the compiler chooses the base template, where value is false.
Now what happens when you define has_type as follows:
template<typename T>
struct has_type { using type = typename T::type; }
Then above specialization needs (in the No case) that a type has_type<No> exists. has_type is a class template, which given some type (No is a type, so everything good) "produces" a type. Thus, has_type<No> is a type. Thus the specialization matches, value is true.
The members of has_type<No> are not needed at this point. You could even use template<typename> struct has_type; (only a declaration, no definition). In other words, it may be an incomplete type:
A template argument for a type template parameter must be a type-id, which may name an incomplete type [..]
http://en.cppreference.com/w/cpp/language/template_parameters
The contents only matter when the compiler actually needs them, e.g. for creating an object of that type:
// Class template with some random members.
template<typename T>
struct Foo {
using baz = typename T::baz;
constexpr static int value = T::value * 42;
};
// Class template which is even only declared
template<typename X> struct Bar; // no definition
// Does not use its template parameter in any way. Needs just a type name.
template<typename> struct Defer {};
int main() {
Defer<Foo<int>> ok;
Defer<Bar<int>> ok_too;
// Foo<int> fail;
// Bar<int> fail_too;
return 0;
}
This mechanism is often used for "type tags", which e.g. can be used to create different types with identical "content" from a single template:
template<typename /* TAG */, typename ValueType>
struct value_of_strong_type {
ValueType value;
// ...
};
struct A_tag; // no definition
using A = value_of_strong_type<A_tag, int>;
struct B_tag; // no definition
using B = value_of_strong_type<B_tag, int>;
Both A and B behave identically, but are not convertible to each other, because they're completely different types.
To make the detector work with such class templates as you showed you need the following specialization:
template<template<class ...> class Op, typename ...Args>
struct Detector<void_t<typename Op<Args...>::type>, Op, Args...> {
// ^^^^^^^^ ^^^^^^
static constexpr bool value = true;
};
Though you cannot just add it, otherwise you run into ambiguous resolution errors.
Consider the following structs:
//Implementations provided elsewhere
struct A { A(int i, double d, std::string s); /* ... */ };
struct B { B(double d1, double d2); /* ... */ };
I have two conversion classes whose template signatures look like:
TupleAs< A, int, double, std::string > via1 { ... };
ArrayAs< B, double, 2 > via2 { ... };
Predictably, TupleAs converts a triplet of int,double, and std::string values into an object of type A. Similarly, ArrayAs converts a pair of two double values into an object of type B. (And yes, there are reasons why I cannot call the A and B constructors directly.)
Improving the syntax
I would like to change the syntax so I can do the following:
TupleAs< A(int,double,std::string) > via1 { ... };
ArrayAs< B(double,2) > via2 { ... };
which, I think, is more descriptive of a conversion process. The TupleAs template declaration and corresponding partial specialization would look like this:
template <typename T> struct TupleAs;
template <typename T, typename ... Args>
struct TupleAs<T(Args...)> { ... };
Compiler errors
However, if I try to do something similar with the ArrayAs version:
template <typename T> struct ArrayAs;
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
I get the following errors in clang (3.6) when trying to instantiate it (ArrayAs< B(double,2)> test;):
typeAs.cpp:14:22: error: unknown type name 'N'
struct ArrayAs<T(U,N)>{
^
typeAs.cpp:14:10: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
struct ArrayAs<T(U,N)>{
^~~~~~~~~~~~~~~
typeAs.cpp:13:45: note: non-deducible template parameter 'N'
template<typename T, typename U, unsigned N>
^
The gcc error diagnostic is a little different, but I won't post it here.
I admit that my templating skills should be better than they are, and I also concede that an analogous std::function<B(double,2)> declaration clearly is nonsense. But can someone tell me why the particular syntax I'm trying to achieve is not allowed? I looked through the C++14 standard and had trouble finding the relevant portion, and I'm having trouble interpreting the clang diagnostic message.
When you specialize TupleAs:
template <typename T, typename ... Args>
struct TupleAs<T(Args...)>
You are basically overloading the notation for a function. You are specializing on a function that takes Args... and returns a T. That is a type. You may not be using that function as a function, or really ever think about it as being a type, but that is what it is.
On the other hand, here:
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
There is no such thing as a function that takes N. It could take unsigned, but it can't take a value. There is just no such reasonable thing. From your example, B(double, 2) simply does not make sense. At best, you could write something that would allow:
template <unsigned N> using size_ = std::integral_constant<size_t, N>;
ArrayAs< B(double,size_<2>) >
Or even:
ArrayAs< B(std::array<double, 2>) >
since now we're back to using types everywhere. Whether you prefer that or not is personal preference.
The key here is that types are first-class citizens when it comes to all things template metaprogramming, and values should be avoided where possible.
template <typename T> struct ArrayAs;
template <typename T, typename U, std::size_t N>
struct ArrayAs<T(std::array<U,N>)> { ... };
works, as would:
template<class T>
struct to_array;
template<class T, size_t N>
struct to_array< T[N] > { using type = std::array<T, N>; };
template<class T>
using arr = typename to_array<T>::type;
then:
ArrayAs< Bob( arr<int[3]> ) > some_var;
live example.
Sadly, directly using ArrayAs< Bob( int[3] ) > doesn't work due to how arrays in function types decay to pointers.
I've implemented a type function Tuple that turn a list of My_enum values into an std::tuple of corresponding types:
#include <tuple>
enum My_enum{ t_int, t_double };
// Bind_type is a type function that given a My_enum returns the corresponding type
template<My_enum E> struct Bind_type;
template<> struct Bind_type<t_int>{ using type = int; };
template<> struct Bind_type<t_double>{ using type = double; };
// Tuple is a type function that given a template value parameter pack of My_enums returns a std::tuple of correspondig types
template<My_enum First, My_enum... Others>
struct Tuple {
using type = decltype(std::tuple_cat(
typename Tuple<First>::type{},
typename Tuple<Others...>::type{}
));
};
template<>
struct Tuple<t_int> {
using type = std::tuple<Bind_type<t_int>::type>;
};
template<>
struct Tuple<t_double> {
using type = std::tuple<Bind_type<t_double>::type>;
};
I'd like to be able to declare the recursion base case for Tuple in one shot, because I don't want to manually manage Tuple specialization as long as I add or remove values to My_enum, because it is error prone (and boring). I've tried:
template<My_enum E>
struct Tuple {
using type = std::tuple<Bind_type<E>::type>;
};
but this is not a valid specialization for the variadic version.
My question is: is there a smart way to declare the specialization of Tuple when it has only one template value parameter?
You can do this without recursion by simply expanding the parameter pack directly into a std::tuple:
template<My_enum... Enums>
struct Tuple {
using type = std::tuple<typename Bind_type<Enums>::type...>;
};
To answer your question more directly, you can declare a variadic primary template, then write two specializations: for when there are at least two parameters, and for when there is only one:
//primary template, takes any number of My_enums
template <My_enum... Enums>
struct Tuple {
//this case will be chosen if we instantiate a Tuple with no args
using type = std::tuple<>;
}
//specialization for when there are at least two arguments
template<My_enum First, My_enum Second, My_enum... Others>
struct Tuple<First,Second,Others...> {
using type = decltype(std::tuple_cat(
typename Tuple<First>::type{},
typename Tuple<Second,Others...>::type{}
));
};
//base case, only one argument left
template<My_enum Last>
struct Tuple<Last> {
using type = std::tuple<typename Bind_type<Last>::type>;
};