Related
How can I cascade variadic types? I.e.:
template <typename... T>
using Cascade = ???; // T1<T2<T3<...>>>
Example:
using Vector2D = Cascade<std::vector, std::vector, double>;
static_assert(std::is_same_v<Vector2D, std::vector<std::vector<double>>>);
You cannot have CascadeRight. T1 is not a typename, it is a template, and so are most of the others, but the last one is a typename. You cannot have different parameter kinds (both types and templates) in the same parameter pack. You also cannot have anything after a parameter pack.
You can have CascadeLeft like this:
template <typename K, template <typename...> class ... T>
class CascadeLeft;
template <typename K>
class CascadeLeft<K>
{
using type = K;
};
template <typename K,
template <typename...> class T0,
template <typename...> class... T>
class CascadeLeft<K, T0, T...>
{
using type = typename CascadeLeft<T0<K>, T...>::type;
};
Frankly, std::vector<std::vector<double>> is much more transparent than CascadeLeft<double, std::vector, std::vector>, so I wouldn't bother.
Expanding on the accepted answer with CascadeRight and support for multiple types for the innermost template:
template<template<typename...> typename Head, template<typename...> typename... Tail>
struct CascadeRight {
template<typename... T>
using type = Head<typename CascadeRight<Tail...>::type<T...>>;
};
template<template<typename...> typename Head>
struct CascadeRight<Head> {
template<typename... T>
using type = Head<T...>;
};
template<template<typename...> typename Head, template<typename...> typename... Tail>
struct CascadeLeft {
template<typename... T>
using type = typename CascadeLeft<Tail...>::type<Head<T...>>;
};
template<template<typename...> typename Head>
struct CascadeLeft<Head> {
template<typename... T>
using type = Head<T...>;
};
using T1 = CascadeRight<std::vector, std::map>::type<int, double>;
using T2 = CascadeLeft<std::map, std::vector>::type<int, double>;
I've created a simple template class called tuple_tag which is identical to std::tuple but only acts as a tag.
// tuple_tag
template <typename...> struct tuple_tag {};
// tuple_tag_element
template <size_t I, typename T>
struct tuple_tag_element;
template <size_t I, typename Head, typename... Tail>
struct tuple_tag_element<I, tuple_tag<Head, Tail...>>
: tuple_tag_element<I - 1, tuple_tag<Tail...>> {};
template <typename Head, typename... Tail>
struct tuple_tag_element<0, tuple_tag<Head, Tail...>>
: std::type_identity<Head> {};
// tuple_tag_element_t
template <size_t I, typename T>
using tuple_tag_element_t = tuple_tag_element<I, T>::type;
// tuple_tag_size
template <typename T>
struct tuple_tag_size;
template <typename T> requires (std::is_reference_v<T> || std::is_const_v<T>)
struct tuple_tag_size<T> : tuple_tag_size<std::remove_cvref_t<T>> {};
template <typename... Ts>
struct tuple_tag_size<tuple_tag<Ts...>>
: std::integral_constant<size_t, sizeof...(Ts)> {};
// tuple_tag_size_v
template <typename T>
inline constexpr size_t tuple_tag_size_v = tuple_tag_size<T>::value;
Here:
using new_type_1 = to_tuple_type<tuple_tag<int, double>>::type;
// new_type_1 = std::tuple<int, double>;
using new_type_2 = to_tuple_tag_type<std::tuple<int, double>>::type;
// new_type_2 = tuple_tag<int, double>;
Where to_tuple_type takes a type template parameter tuple_tag<...> which will be converted into type std::tuple<...>, and to_tuple_tag_type takes a type template parameter std::tuple<...> which will be converted into type tuple_tag<...>.
What I am trying to achieve here is to pass all type template parameters from tuple_tag into std::tuple and vice-versa.
This is my prototype for to_tuple_type where it fails:
template <typename TupleTag>
struct to_tuple_type {
using type = std::tuple<...>;
};
Where type alias will be expanded into:
using type = std::tuple<tuple_tag_element_t<Index, TupleTag>...>;
...
using type = std::tuple<
tuple_tag_element_t<0, TupleTag>,
tuple_tag_element_t<1, TupleTag>,
...,
tuple_tag_element_t<N - 1, TupleTag>
>;
Where N is equal to tuple_tag_size_v<TupleTag>.
What I could only think of is to use std::index_sequence but I don't know where do I introduce the pack.
3 steps. First, make a pack
using indexes=std::make_index_sequence<tuple_tag_size<TupleTag>;
then have a helper that expands the pack. I like this one:
template<auto x>
using constant_t=std::integral_constant<decltype(x),x>;
template<auto x>
constexpr constant_t<x> constant={};
template<std::size_t...Is>
constexpr auto all_indexes( std::index_sequence<Is...> ){
return [](auto f){
return f(constant<Is>...);
};
}
now we can
template<class T>
struct tag_t{using type=T;};
template<class T>
constexpr tag_t<T> tag={};
using type=typename decltype(all_indexes(indexes{})([](auto...Is){
return tag<std::tuple<tuple_tag_element_t<Is, TupleTag>...>;
}))::type;
if that has no tpyos.
There is a simple solution that applies partial template specialization:
// to_tuple_type
template <typename Tup>
struct to_tuple_type;
template <typename... Ts>
struct to_tuple_type<tuple_tag<Ts...>> : std::type_identity<std::tuple<Ts...>> {};
// to_tuple_type_t
template <typename Tup>
using to_tuple_type_t = to_tuple_type<Tup>::type;
// to_tuple_tag_type
template <typename Tup>
struct to_tuple_tag_type;
template <typename... Ts>
struct to_tuple_tag_type<std::tuple<Ts...>> : std::type_identity<tuple_tag<Ts...>> {};
// to_tuple_tag_type_t
template <typename Tup>
using to_tuple_tag_type_t = to_tuple_tag_type<Tup>::type;
Let's suppose I have a type T and I want to detect whether it has a subscript operator which I can call with with another type Index. The following example works just fine:
#include <type_traits>
#include <vector>
template < typename T, typename Index >
using subscript_t = decltype(std::declval<T>()[std::declval<Index>()]);
int main()
{
using a = subscript_t< std::vector<int>, size_t >;
using b = subscript_t< std::vector<int>, int >;
}
However, I want the function to be detected if and only if the function signature matches exactly. In the example above I would like the statement subscript_t< std::vector<int>, int >; to throw an error like no viable overloaded operator[], because the signature of the subscript operator for std::vector is
std::vector<T, std::allocator<T>>::operator[](size_type pos);
where size_type in GCC is unsigned long. How can I avoid the implicit conversion from int to size_t to take place?
With is_detected, you may do:
template <typename T, typename Ret, typename Index>
using subscript_t = std::integral_constant<Ret (T::*) (Index), & T::operator[]>;
template <typename T, typename Ret, typename Index>
using has_subscript = is_detected<subscript_t, T, Ret, Index>;
static_assert(has_subscript<std::vector<int>, int&, std::size_t>::value, "!");
static_assert(!has_subscript<std::vector<int>, int&, int>::value, "!");
Demo
I wrote this in SO Documentation:
is_detected
To generalize type_trait creation:based on SFINAE
there are experimental traits detected_or, detected_t, is_detected.
With template parameters typename Default, template <typename...> Op and typename ... Args:
is_detected: alias of std::true_type or std::false_type depending of the validity of Op<Args...>
detected_t: alias of Op<Args...> or nonesuch depending of validity of Op<Args...>.
detected_or: alias of a struct with value_t which is is_detected, and type which is Op<Args...> or Default depending of validity of Op<Args...>
which can be implemented using std::void_t for SFINAE as following:
namespace detail {
template <class Default, class AlwaysVoid,
template<class...> class Op, class... Args>
struct detector
{
using value_t = std::false_type;
using type = Default;
};
template <class Default, template<class...> class Op, class... Args>
struct detector<Default, std::void_t<Op<Args...>>, Op, Args...>
{
using value_t = std::true_type;
using type = Op<Args...>;
};
} // namespace detail
// special type to indicate detection failure
struct nonesuch {
nonesuch() = delete;
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
void operator=(nonesuch const&) = delete;
};
template <template<class...> class Op, class... Args>
using is_detected =
typename detail::detector<nonesuch, void, Op, Args...>::value_t;
template <template<class...> class Op, class... Args>
using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;
template <class Default, template<class...> class Op, class... Args>
using detected_or = detail::detector<Default, void, Op, Args...>;
Traits to detect presence of method can then be simply implemented:
template <typename T, typename ...Ts>
using foo_type = decltype(std::declval<T>().foo(std::declval<Ts>()...));
struct C1 {};
struct C2 {
int foo(char) const;
};
template <typename T>
using has_foo_char = is_detected<foo_type, T, char>;
static_assert(!has_foo_char<C1>::value, "Unexpected");
static_assert(has_foo_char<C2>::value, "Unexpected");
static_assert(std::is_same<int, detected_t<foo_type, C2, char>>::value,
"Unexpected");
static_assert(std::is_same<void, // Default
detected_or<void, foo_type, C1, char>>::value,
"Unexpected");
static_assert(std::is_same<int, detected_or<void, foo_type, C2, char>>::value,
"Unexpected");
You can do this:
template <typename Index, typename ClassType, typename ReturnType>
constexpr bool arg_t(ReturnType (ClassType::*)(Index))
{
return true;
}
template <typename T, typename Index>
struct subscript_t
{
static_assert(arg_t<Index>(&T::operator[]));
};
Note that you'll need to instantiate subscript_t, not merely name its type:
int main()
{
subscript_t< std::vector<int>, size_t > a;
subscript_t< std::vector<int>, int > b;
}
Another way, which lets you determine the exact error message:
template <typename ClassType, typename ReturnType, typename ArgType>
constexpr ArgType arg_t(ReturnType (ClassType::*)(ArgType))
{
return {};
}
template <typename T, typename Index>
struct subscript_t
{
using Actual = decltype(arg_t(&T::operator[]));
static_assert(std::is_same<Index, Actual>::value, "oops");
};
Please consider the following code snippet:
template<typename T, class Tuple>
class vector
{
using size_type = typename Tuple::size_type;
template<typename... Elements,
typename = decltype(std::declval<Tuple>().reserve(size_type()))>
typename = decltype(std::declval<Tuple>().push_back(T())),
vector(Elements&&... elements)
{ /* ... */ }
};
I want to define a nested struct supports_reserve_push_back which is derived from std::true_type whenever the constructor above would be enabled (and which is derived from std::false_type in the other case).
How can I do this?
I've modified the code to make it build. And implemented the trait you requested, to the best of my understanding.
#include <iostream>
#include <type_traits>
#include <vector>
#include <map>
namespace example {
template<typename...>
using void_t = void;
template<typename T, class Tuple>
struct vector {
using size_type = typename Tuple::size_type;
using tuple_type = Tuple;
using elem_type = T;
template<typename... Elements>
vector(Elements&&... elements)
{ /* ... */ }
};
template <class T, typename = void>
struct supports_reserve_push_back : std::false_type {};
template <class Vec>
struct supports_reserve_push_back<Vec, void_t<
decltype(std::declval<typename Vec::tuple_type>().reserve(typename Vec::size_type())),
decltype(std::declval<typename Vec::tuple_type>().push_back(typename Vec::elem_type())) >
>
: std::true_type {};
}
int main() {
std::cout
<< example::supports_reserve_push_back<example::vector<int, std::vector<int>>>::value
<< '\n'
<< example::supports_reserve_push_back<example::vector<int, std::map<int, int>>>::value;
return 0;
}
A few thing to note:
The way you wrote the c'tor originally caused a hard error when instantiating the class in the negative case. That's why I removed the chcck from the c'tor.
I'd suggest you define the type traits first, and use them to enable your c'tors.
namespace details{
template<template<class...>class Z,class,class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z,class...Ts>
struct can_apply<Z,std::void_t<Z<Ts...>,Ts...>:
std::true_type{};
}
template<template<class...>class Z,class... Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
Wrap up your decltypes into template usings and do some && and done.
There is also a std experimental similar to above.
template<class T, class U>
using push_back_r = decltype(std::declval<T>().push_back(std::declval<U>()));
template<class T>
using reserve_r = decltype(std::declval<T>().reserve(1));
template<class T, class U>
constexpr can_apply<push_back_r,T,U> has_push_back={};
template<class T>
constexpr can_apply<reserve_r,T> has_reserve={};
template<bool b>using bool_t=std::integral_constant<bool,b>;
template<class T,class U>
constexpr bool_t<has_push_back<T,U>&&has_reserve<T>>
efficiently_fillable_with = {};
Then efficiently_fillable_with<T,U> is true type iff you can reserve space with T and then push Us into it. The r/l value category of T and U is preserved: if you want to know about filling an non-cinstant lvalue of T with rvalue Us:
efficiently_fillable_with<T&,U>
If you want to fill with U const& instead of rvalues, pass U const&.
#include <type_traits>
#include <utility>
template <typename...>
using void_t = void;
template <typename AlwaysVoid, template <typename...> class Operation, typename... Args>
struct detect_impl : std::false_type {};
template <template <typename...> class Operation, typename... Args>
struct detect_impl<void_t<Operation<Args...>>, Operation, Args...> : std::true_type {};
template <template <typename...> class Operation, typename... Args>
using detect = detect_impl<void_t<>, Operation, Args...>;
template <typename T, typename Sz>
using has_reserve = decltype(std::declval<T>().reserve(std::declval<Sz>()));
template <typename T, typename U>
using has_push_back = decltype(std::declval<T>().push_back(std::declval<U>()));
template <typename Tuple, typename T, typename size_type>
constexpr bool supports_reserve_push_back = detect<has_reserve, Tuple, size_type>{} && detect<has_push_back, Tuple, T>{};
Test:
template <typename T, class Tuple>
class vector
{
public:
using size_type = typename Tuple::size_type;
template <typename... Elements, typename U = Tuple,
std::enable_if_t<supports_reserve_push_back<U&, T, size_type>, int> = 0>
vector(Elements&&... elements)
{
}
template <typename... Elements, typename U = Tuple,
std::enable_if_t<!supports_reserve_push_back<U&, T, size_type>, int> = 0>
vector(Elements&&... elements)
{
}
};
DEMO
I would try the following approach:
Have your vector template class inherit from a superclass, like this:
template<typename T, class Tuple> class vector
: public supports_reserve_push_back_impl<
vector_has_default_constructor<T, Tuple>::value() > {
// ...
}
Now, define a vector_has_default_constructor template class that takes the same template parameters:
template<typename T, class Tuple> class vector_has_default_constructor {
public:
// ...
};
In vector_has_default_constructor:
Define a constexpr bool value() method with the same exact signature as the vector's constructor. This constexpr method returns true.
Define an overload constexpr bool value() with a ... signature, which should have lower priority in the overload resolution. This constexpr returns false.
Now, this situation is reduced to defining two trivial specializations, supports_reserve_push_back_impl<true> and supports_reserve_push_back_impl<false>.
supports_reserve_push_back_impl<true> contains your desired supports_reserve_push_back value, and is inherited by your vector.
supports_reserve_push_back_impl<false> is empty.
Consider this fully working code:
#include <type_traits>
template <typename T, typename IndexPack> struct Make;
template <typename T, template <T...> class P, T... Indices>
struct Make<T, P<Indices...>> {
using type = P<(Indices+1)..., (-3*Indices)..., (Indices-1)...>;
};
template <int...> class Pack;
int main() {
static_assert (std::is_same<Make<int, Pack<1,2,3,4>>::type,
Pack<2,3,4,5, -3,-6,-9,-12, 0,1,2,3>>::value, "false");
}
What I actually want the output to be is
Pack<2,-3,0, 3,-6,1, 4,-9,2, 5,-12,3>
instead of Pack<2,3,4,5, -3,-6,-9,-12, 0,1,2,3>. I first tried
using type = P<(Indices+1, -3*Indices, Indices-1)...>;
but that is simply understood by the compiler to be a useless comma operator. What is the desired syntax to get what I want? If there is no such syntax, what is the cleanest way to do this, keeping in mind that using Indices 3 times is just an example (we may want to use it more than 3 times). Please don't tell me that I have to write a helper to extract the individual packs and then "interlace" all the elements. That nightmarish method cannot be the best solution (and such a solution would also only work if we knew exactly how many individual packs to extract).
Would defining
template <typename T, template <T...> class P, T I>
struct Component {
using type = P<I+1, -3*I, I-1>;
};
help somehow? Make a pack expansion on this?
Yes, you can concat recursively:
template <typename, typename, typename> struct Concat;
template <typename T, template <T...> class P, T... A, T... B>
struct Concat<T, P<A...>, P<B...>> {
using type = P<A..., B...>;
};
template <typename T, typename IndexPack> struct Make;
template <typename T, template <T...> class P, T... I, T F >
struct Make<T, P<F, I...>> {
using type = typename Concat<T,
typename Make<T, P<F>>::type,
typename Make<T, P<I...>>::type>::type;
};
template <typename T, template <T...> class P, T I>
struct Make<T, P<I>> {
using type = P<I+1, -3*I, I-1>;
};
Demo
This was inspired by Columbo's solution. It uses the pack expansion syntax that I originally sought, namely
using type = typename Merge<T, typename Component<T, P, Indices>::type...>::type;
As a result, now Make is reusable, first using Triple, and then using Quadruple, so any number of usages of Indices can be expanded simultaneously. Here Component is a template-template-template parameter passed into Make:
#include <type_traits>
template <typename T, typename... Packs> struct Merge;
template <typename T, template <T...> class P1, template <T...> class P2, T... Is, T... Js>
struct Merge<T, P1<Is...>, P2<Js...>> {
using type = P1<Is..., Js...>;
};
template <typename T, typename Pack1, typename Pack2, typename... Packs>
struct Merge<T, Pack1, Pack2, Packs...> {
using type = typename Merge<T, Pack1, typename Merge<T, Pack2, Packs...>::type>::type;
};
template <typename T, template <T...> class P, T I>
struct Triple {
using type = P<I+1, -3*I, I-1>;
};
template <typename T, template <T...> class P, T I>
struct Quadruple {
using type = P<I+1, -3*I, I-1, I>;
};
template <typename T, typename IndexPack,
template <typename U, template <U...> class P, U I> class Component> struct Make;
template <typename T, template <T...> class Z, T... Indices,
template <typename U, template <U...> class P, U I> class Component>
struct Make<T, Z<Indices...>, Component> {
using type = typename Merge<T, typename Component<T, Z, Indices>::type...>::type;
};
template <int...> class Pack;
int main() {
static_assert (std::is_same<Make<int, Pack<1,2,3,4>, Triple>::type,
Pack<2,-3,0, 3,-6,1, 4,-9,2, 5,-12,3>>::value, "false");
static_assert (std::is_same<Make<int, Pack<1,2,3,4>, Quadruple>::type,
Pack<2,-3,0,1, 3,-6,1,2, 4,-9,2,3, 5,-12,3,4>>::value, "false");
}