Is there a way to implement universal and existential quantification using C++ template magic (maybe using SFINAE etc.)? Something like this:
template
<
template <typename Argument> class Predicate
>
struct UniversalQuantification
{
static const bool value =
/*for any Argument Predicate<Argument>::value == true ? true : false*/;
};
template
<
template <typename Argument> class Predicate
>
struct ExistentialQuantification
{
static const bool value =
/*for some Argument Predicate<Argument>::value == true ? true : false*/;
};
If you pass a finite set of possible template arguments to the template, it is indeed possible to evaluate this at compile-time. However, it is impossible to evaluate this for every argument the passed template has been used with, as these types/args are unknown.
This is the solution for a finite set of arguments for the ExistentialQuantification class. In order to achieve the behaviour of the UniversalQuantification class, you simple have to change the || to a &&:
template<typename Arg>
struct Pred1;
template<>
struct Pred1<float> { static const bool value = true; };
template<>
struct Pred1<double> { static const bool value = false; };
template<>
struct Pred1<long> { static const bool value = true; };
template<template <typename Argument> class Predicate, typename... Types>
struct ExistentialQuantification;
template<template <typename Argument> class Predicate, typename Arg>
struct ExistentialQuantification<Predicate, Arg>
{
static const bool value = Predicate<Arg>::value;
};
template<template <typename Argument> class Predicate, typename Arg, typename... Types>
struct ExistentialQuantification<Predicate, Arg, Types...>
{
static const bool value = Predicate<Arg>::value || ExistentialQuantification<Predicate, Types...>::value;
};
int main()
{
std::cout << ExistentialQuantification<Pred1, long, double, float>::value << std::endl;
}
In this example, value will evaluate to true || false || true and is thus true of course.
Ok, if we're just being smart here, and if we're allowed a couple of constraints, here are two constraints on the programmer, that really solve the problem, smoothly.
if a predicate is needed in the code that is always true, only use the following:
template<typename> struct always : std::true_type { };
if a predicate is needed in the code that is never true, only use the following:
template<typename> struct never : std::false_type { };
Now, the solution is simple:
template<template<typename> class>
struct UniversalQuantification : std::false_type { };
template<>
struct UniversalQuantification<always> : std::true_type { };
template<template<typename> class>
struct ExistentialQuantification : std::true_type { };
template<>
struct ExistentialQuantification<never> : std::false_type { };
Using a library like Boost.MPL, if you can put your universe of allowed types into a type list, e.g. in a boost::mpl::vector, then your quantifiers are just the compile-time version of std::all_of and std::any_of. You can define them as:
#include <ios>
#include <iostream>
#include <type_traits> // is_same, is_base_of
#include <boost/mpl/end.hpp> // end
#include <boost/mpl/find_if.hpp> // find_if
#include <boost/mpl/lambda.hpp> // lambda
#include <boost/mpl/logical.hpp> // not_
#include <boost/mpl/placeholders.hpp> // _1
#include <boost/mpl/vector.hpp> // vector
template<typename Sequence, typename Pred>
struct all_of
:
std::is_same< typename
boost::mpl::find_if<
Sequence,
boost::mpl::not_<Pred>
>::type, typename
boost::mpl::end<Sequence>::type
>
{};
template<typename Sequence, typename Pred>
struct none_of
:
all_of< Sequence, boost::mpl::not_< Pred > >
{};
template<typename Sequence, typename Pred>
struct any_of
:
boost::mpl::not_< none_of< Sequence, Pred > >
{};
struct B {};
struct D : B {};
struct X {};
using Universe = boost::mpl::vector<B, D, X>;
using Predicate = boost::mpl::lambda<std::is_base_of<B, boost::mpl::_1>>;
int main()
{
std::cout << std::boolalpha;
std::cout << all_of<Universe, Predicate>{} << "\n";
std::cout << any_of<Universe, Predicate>{} << "\n";
}
Live Example.
As you can see, because X does not have B as a base class, the universal quantification fails, but because D does have B as a base class, the existential quantification succeeds.
Well, if we're just talking about n-ary (variadic) functions and, or as in the answer by TemplateRex, here's my preferred way without Boost:
using _true = std::integral_constant <bool, true>;
using _false = std::integral_constant <bool, false>;
template <bool C, typename T, typename E>
using _if = typename std::conditional <C, T, E>::type;
template <typename...> struct _and;
template <typename...> struct _or;
template <typename A, typename... B>
struct _and <A, B...> : _if <A{}, _and <B...>, _false> { };
template <typename A, typename... B>
struct _or <A, B...> : _if <A{}, _true, _or <B...> > { };
template <> struct _and <> : _true { };
template <> struct _or <> : _false { };
Related
I want to create a few template classes in order to assert certain type of data:
assertType { static constexpr bool v{false}; };
template<> struct assertType<int> { static constexpr bool v{true}; };
template<> struct assertType<bool> { static constexpr bool v{true}; };
template<>struct assertType<float> { static constexpr bool v{true}; };
template<>struct assertType<long> { static constexpr bool v{true}; };
However, I would like to do it programmatically, So I thought about defining a "list" of the supported types:
template<typename ...Types>
struct TypesList { };
static inline TypesList<int, bool, float, long> supportedTypes;
And have that "list" passed to another template that, by recursion, generates the "assertType" template for every type in the list. Something like:
template<typename ...Ts>
struct BuildTemplates { };
template<typename ...Ts>
struct BuildTemplates<TypesList<Ts...>> { };
BuildTemplates<supportedTypes> /* Build the templates for int, bool, float and long */
So I can use:
assertType<int>::v // v should be true
assertType<bool>::v // v should be true
assertType<float>::v // v should be true
assertType<long>::v // v should be true
Any other template whose type is not one of those, should have its v value set to false.
Is this possible?
Thanks in advance.
The answer mostly depends on the syntax you want to get. For example, you could do this:
#include <type_traits>
template<typename... Types> struct TypeList {};
using SupportedTypes = TypeList<int, bool, float, long>;
template<typename T>
struct assertType {
template<class... Types>
static constexpr bool contains(TypeList<Types...>) {
return (std::is_same_v<T, Types> || ...);
}
static constexpr bool v = contains(SupportedTypes{});
};
static_assert( assertType<int>::v);
static_assert( assertType<bool>::v);
static_assert(!assertType<short>::v);
If you can use boost, boost::mp11::mp_contains could be employed to make the implementation trivial:
#include <boost/mp11.hpp>
using SupportedTypes = boost::mp11::mp_list<int, bool, float, long>;
template<typename T>
struct assertType : boost::mp11::mp_contains<SupportedTypes, T> {};
static_assert( assertType<int>::value);
static_assert( assertType<bool>::value);
static_assert(!assertType<short>::value);
Or if you want to use v instead of value (which is de facto a standard name):
template<typename T>
struct assertType {
static constexpr bool v =
boost::mp11::mp_contains<SupportedTypes, T>::value;
};
This is only possible if your programmatic solution is allowed to define a template specialization of assertType, or to be used within its template definition.
Here's an approach by defining a single partial template specialization of assertType in c++11:
template <typename>
struct assertType { static constexpr bool v = false; };
template <typename...>
struct TypeList {};
using supportedTypes = TypesList<int, bool, float, long>;
#include <type_traits>
template <typename...>
struct any_of : std::false_type {};
template <typename T, typename U, typename... Us>
struct any_of<T, TypeList<U, Us...>> : any_of<T, TypeList<Us...>> {};
template <typename T, typename... Us>
struct any_of<T, TypeList<T, Us...>> : std::true_type {};
template <typename T>
struct assertType<typename std::enable_if<any_of<T, supportedTypes>, T>::type> {
static constexpr bool v = true;
};
You could also just use it within the class template definition itself rather than defining a partial specialization:
// assuming supportedTypes and any_of have already been defined
template <typename T>
struct assertType { static constexpr bool v = any_of<T, supportedTypes>::value; };
In c++17 the definition of any_of becomes much simpler with a fold expression and std::bool_constant:
#include <type_traits>
template <typename...>
struct any_of : std::false_type {};
template <typename T, typename... Us>
struct any_of<T, TypeList<Us...>> : std::bool_constant<(... || std::is_same_v<T, Us>)> {};
Or by using std::disjunction:
#include <type_traits>
template <typename...>
struct any_of : std::false_type {};
template <typename T, typename... Us>
struct any_of<T, TypeList<Us...>> : std::disjunction<std::is_same_v<T, Us>...> {};
Finally in c++20, you can simplify the definition of the assertType partial template specialization using concepts:
// assuming supportedTypes and any_of have already been defined
template <typename T, typename L>
concept any_of_v = any_of<T, L>::value;
template <typename>
struct assertType { static constexpr bool v = false; };
template <any_of_v<supportedTypes> T>
struct assertType<T> { static constexpr bool v = true; };
Except for instantiating a template or preprocessor trickery, there is no way to generate declarations in C++ automatically. Each declaration must be written out explicitly.
But there is no need to have an explicit specialization for each type in the list. You can simply use a single primary template for assertType and set the v member according to membership in the list or you can use a fixed number of partial specializations. The other answers give approaches to do this.
Suppose you have something like this:
template<class D>
class HasDef {
public:
typedef D Def;
};
class A : public HasDef<class B> {};
class B : public HasDef<class C> {};
class C {};
So it is like a "metaprogramming linked list", with type links, via the included typedef Def. Now I want to make a template "Leaf" that, when applied to A, follows the links to yield C:
void f() {
Leaf<A>::type v; // has type C
}
Is it even possible at all to do this? I've tried some methods with std::compare and similar, but none are valid code: everything seems to run into issues with either that C has no Def typedef, or else that the type Leaf<> itself is incomplete when the inner recursive "call" is made so it (or its internal type type) cannot be referenced.
FWIW, the reason I want this is for making a "hierarchical state machine" where that Def represents the default state for each state in the hierarchy, and something a bit more elaborate that this seems to provide a fairly neat and clean "user interface syntax" for it.
I don't really like f(...) in modern code, thus my version uses void_t from C++17:
#include <type_traits>
template<class D>
struct HasDef {
typedef D Def;
};
struct A : HasDef<class B> {};
struct B : HasDef<class C> {};
struct C {};
template <typename T, typename=void>
struct DefPresent : std::false_type{};
template <typename T>
struct DefPresent<T, std::void_t<typename T::Def>> : std::true_type{};
template<typename T, bool deeper = DefPresent<T>::value>
struct Leaf
{
using Type = typename Leaf<typename T::Def>::Type;
};
template<typename T>
struct Leaf<T, false >
{
typedef T Type;
};
static_assert(std::is_same<typename Leaf<C>::Type, C>::value, "C");
static_assert(std::is_same<typename Leaf<B>::Type, C>::value, "B");
static_assert(std::is_same<typename Leaf<A>::Type, C>::value, "A");
https://godbolt.org/z/5h5rfe81o
EDIT: just for completenes, 2 C++20 variants utilizing concepts. Tested on GCC 10
#include <type_traits>
#include <concepts>
template<class D>
struct HasDef {
typedef D Def;
};
struct A : HasDef<class B> {};
struct B : HasDef<class C> {};
struct C {};
template <typename T>
concept DefPresent = requires(T a)
{
typename T::Def;
};
template<typename T>
struct Leaf
{
using Type = T;
};
template<typename T>
requires DefPresent<T>
struct Leaf<T>
{
using Type = Leaf<typename T::Def>::Type;
};
static_assert(std::is_same_v<typename Leaf<C>::Type, C>, "C");
static_assert(std::is_same_v<typename Leaf<B>::Type, C>, "B");
static_assert(std::is_same_v<typename Leaf<A>::Type, C>, "A");
template<typename T>
struct Leaf2
{
template <typename M>
static M test(M&&);
template <DefPresent M>
static auto test(M&&) -> typename Leaf2<typename M::Def>::Type;
using Type = decltype(test(std::declval<T>()));
};
static_assert(std::is_same<typename Leaf2<C>::Type, C>::value, "C");
static_assert(std::is_same<typename Leaf2<B>::Type, C>::value, "B");
static_assert(std::is_same<typename Leaf2<A>::Type, C>::value, "A");
https://godbolt.org/z/vcqEaPrja
You can implement this with SFINAE to separate types that have the typedef from ones that don't when trying to follow them to their leaf. I used the trick from this SO answer here.
The first implementation for Leaf follows the typedef recursively, the second one just defines the type itself as there is no typedef to follow.
Also note I changed your classes to struct for default-public inheritance. Also I changed the order of their definitions for this to compile.
Compiler explorer
#include <type_traits>
namespace detail
{
template<typename T> struct contains_def {
template<typename U> static char (&test(typename U::Def const*))[1];
template<typename U> static char (&test(...))[2];
static const bool value = (sizeof(test<T>(0)) == 1);
};
template<typename T, bool has = contains_def<T>::value>
struct Leaf {
using type = typename Leaf<typename T::Def>::type;
};
template<typename T>
struct Leaf<T, false> {
using type = T;
};
}
template <typename T>
using Leaf = detail::Leaf<T>; // expose Leaf to the global scope
template <typename T>
using Leaf_t = typename Leaf<T>::type; // for convenience
template<typename T>
struct AddDef {
using Def = T;
};
struct C {};
struct B : AddDef<C> {};
struct A : AddDef<B> {};
static_assert(std::is_same_v<Leaf_t<A>, C>);
static_assert(std::is_same_v<Leaf_t<B>, C>);
static_assert(std::is_same_v<Leaf_t<C>, C>);
You could make it a type trait:
#include <utility> // declval
template<class L>
struct Leaf {
template<class M>
static L test(const M&); // match L's without Def
template<class M> // match L's with Def
static auto test(M&&) -> typename Leaf<typename M::Def>::type;
// find matching test() overload, prefer test(M&&):
using type = decltype( test(std::declval<L>()) );
};
template<class L> // bonus helper types
using Leaf_t = typename Leaf<L>::type;
Demo and template resolution # cppinsights
Say I was trying to implement a concept meowable that
Integral types are meowable.
Class types with member function meow are meowable. This is in the final target but the current question doesn't focus on it.
Tuple-like types with only meowable elements are meowable.
std::ranges::range with meowable elements are meowable. This is in the final target but the current question doesn't focus on it.
Then I came up with this implementation(simplified as I could):
#include <concepts>
#include <type_traits>
#include <ranges>
#include <utility>
#include <tuple>
template<class T>
concept meowable_builtin = std::integral<T>;
template<class T, std::size_t I>
concept has_tuple_element = requires (T t) {
typename std::tuple_element<I, T>::type;
{ get<I>(t) } -> std::convertible_to<std::tuple_element_t<I, T>&>;
};
template<class T>
concept tuple_like = requires {
typename std::tuple_size<T>::type;
{ std::tuple_size_v<T> } -> std::convertible_to<std::size_t>;
} &&
[]<std::size_t...I>(std::index_sequence<I...>) {
return (has_tuple_element<T, I> && ...);
} (std::make_index_sequence<std::tuple_size_v<T>>{});
template<class T> struct is_meowable: std::false_type{};
template<meowable_builtin T> struct is_meowable<T>: std::true_type{};
template<tuple_like T>
struct is_meowable<T>
: std::bool_constant<
[]<std::size_t...I>(std::index_sequence<I...>) {
return (is_meowable<std::tuple_element_t<I, T>>::value && ...);
} (std::make_index_sequence<std::tuple_size_v<T>>{})
> {};
template<class T>
concept meowable_tuple = tuple_like<T> && is_meowable<T>::value;
template<class T>
concept meowable = is_meowable<T>::value;
static_assert(meowable<int>);
//static_assert(tuple_like<std::tuple<int>>);
static_assert(is_meowable<std::tuple<int>>::value);
But some compilers don't like it (https://godbolt.org/z/5vMTEhTdq):
1. GCC-12 and above: internal compiler error.
2. GCC-11: accepted.
3. Clang-13 and above: static_assert fired.
4. MSVC-v19: accepted.
However, if I uncomment the second last line of code, all compilers are happy. (Instantiation point of concepts?)
So my questions are:
Why this behavior? (compiler bug or something like "ill-formed NDR"?)
How can I achieve my target?
Why this behavior? (compiler bug or something like "ill-formed NDR"?)
This is apparently a bug of GCC-trunk and Clang-trunk, the issue here is that GCC/Clang doesn't properly handle the template partial specialization based on the concept initialized by the lambda. Reduced
template<class>
concept C = [] { return true; } ();
template<class T>
struct S {};
template<class T>
requires C<T>
struct S<T> { constexpr static bool value = true; };
// static_assert(C<int>);
static_assert(S<int>::value);
How can I achieve my target?
Replace lambda with the template function based on the reduced result
template<class T, std::size_t...I>
constexpr bool all_has_tuple_element(std::index_sequence<I...>) {
return (has_tuple_element<T, I> && ...);
}
template<class T>
concept tuple_like = requires {
typename std::tuple_size<T>::type;
{ std::tuple_size_v<T> } -> std::convertible_to<std::size_t>;
} && all_has_tuple_element<T>(std::make_index_sequence<std::tuple_size_v<T>>{});
Demo
I wrote a GENERIC version (using SFINAE) with customizable predicate that will work with all the specialized tuple-like types (those that have specializations for std::tuple_element and std::tuple_size).
https://godbolt.org/z/Yf889GY6E
#include <cstddef>
#include <type_traits>
#include <tuple>
template <typename, typename = void>
struct is_complete : std::false_type {};
template <typename T>
struct is_complete<T, std::void_t<decltype(sizeof(T))>> : std::true_type {};
// catching all standard (and specialized) tuple-like types
template <typename T>
struct is_tuple_like : is_complete<std::tuple_size<T>> {};
template <typename T, bool = is_tuple_like<T>::value>
struct get_tuple_size
{
constexpr static size_t value = 0;
};
template <typename T>
struct get_tuple_size<T, true>
{
constexpr static size_t value = std::tuple_size_v<T>;
};
// generic solution with predicate
template <template <typename> class PredicateT,
typename T,
bool = is_tuple_like<T>::value,
typename = decltype(std::make_index_sequence<get_tuple_size<T>::value>{})>
struct tuple_conjunction : PredicateT<T> {};
template <template <typename> class PredicateT,
typename T,
size_t ... I>
struct tuple_conjunction<PredicateT, T, true, std::index_sequence<I...>> :
std::conjunction<tuple_conjunction<PredicateT, std::tuple_element_t<I, T>>...> {};
////////////////////////////
template <typename T, typename = void>
struct has_meow : std::false_type {};
template <typename T>
struct has_meow<T, std::void_t<decltype(std::declval<T>().mew())>> : std::true_type {};
template <typename T>
using meowable_pred = std::disjunction<std::is_integral<T>, has_meow<T>>;
template <typename T>
using meowable = tuple_conjunction<meowable_pred, T>;
#include <array>
struct cat
{
void mew() {}
};
struct dummy{};
int main()
{
static_assert(!is_complete<std::tuple_element<0, cat>>::value);
static_assert(is_complete<std::tuple_element<0, std::tuple<cat>>>::value);
static_assert(meowable<int>::value);
static_assert(meowable<cat>::value);
static_assert(!meowable<dummy>::value);
static_assert(meowable<std::tuple<long>>::value);
static_assert(meowable<std::tuple<int, long>>::value);
static_assert(meowable<std::tuple<cat, long>>::value);
static_assert(meowable<std::tuple<int, std::tuple<cat, long>>>::value);
static_assert(!meowable<std::tuple<int, std::tuple<cat, long>, dummy>>::value);
// std::array
static_assert(meowable<std::array<cat, 42>>::value);
static_assert(meowable<std::tuple<int, std::tuple<cat, long, std::tuple<std::array<cat, 42>>>>>::value);
return 0;
};
With some adjustments it will also work with C++11.
You can provide any predicate you want for a recursive check. The example shows a usage with meowable_pred predicate.
OLD ANSWER
Here is a simple recursive SFINAE solution for C++17 (and 11 for some adjustments):
https://godbolt.org/z/cezqhb99b
#include <type_traits>
template <typename T, typename = void>
struct has_meow : std::false_type {};
// this is a customization point to deduce tuple-like traits
// you can define some default logic here,
// but for the sake of example let it be false by default
template <typename>
struct is_tuple_like : std::false_type {};
template <typename T>
struct has_meow<T, std::void_t<decltype(std::declval<T>().mew())>> : std::true_type {};
template <typename T>
struct meowable : std::disjunction<std::is_integral<T>, has_meow<T>> {};
template <template <typename...> class TupleT, typename ... T>
struct meowable<TupleT<T...>> : std::conjunction<
is_tuple_like<TupleT<T...>>,
std::conjunction<meowable<T>...
> {};
#include <tuple>
#include <array>
// this will also catch std::pair
template <typename ... T>
struct is_tuple_like<std::tuple<T...>> : std::true_type {};
template <typename T, size_t N>
struct is_tuple_like<std::array<T, N>> : std::true_type {};
struct cat
{
void mew() {}
};
int main()
{
static_assert(meowable<int>::value);
static_assert(meowable<std::tuple<int, long>>::value);
static_assert(meowable<cat>::value);
static_assert(meowable<std::tuple<cat, long>>::value);
static_assert(meowable<std::tuple<int, std::tuple<cat, long>>>::value);
return 0;
};
Is it possible to write a type trait whose value is true for all common STL structures (e.g., vector, set, map, ...)?
To get started, I'd like to write a type trait that is true for a vector and false otherwise. I tried this, but it doesn't compile:
template<class T, typename Enable = void>
struct is_vector {
static bool const value = false;
};
template<class T, class U>
struct is_vector<T, typename boost::enable_if<boost::is_same<T, std::vector<U> > >::type> {
static bool const value = true;
};
The error message is template parameters not used in partial specialization: U.
Look, another SFINAE-based solution for detecting STL-like containers:
template<typename T, typename _ = void>
struct is_container : std::false_type {};
template<typename... Ts>
struct is_container_helper {};
template<typename T>
struct is_container<
T,
std::conditional_t<
false,
is_container_helper<
typename T::value_type,
typename T::size_type,
typename T::allocator_type,
typename T::iterator,
typename T::const_iterator,
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end()),
decltype(std::declval<T>().cbegin()),
decltype(std::declval<T>().cend())
>,
void
>
> : public std::true_type {};
Of course, you might change methods and types to be checked.
If you want to detect only STL containers (it means std::vector, std::list, etc) you should do something like this.
UPDATE. As #Deduplicator noted, container might not meet AllocatorAwareContainer requirements (e.g.: std::array<T, N>). That is why check on T::allocator_type is not neccessary. But you may check any/all Container requirements in a similar way.
You would say that it should be simpler than that...
template <typename T, typename _ = void>
struct is_vector {
static const bool value = false;
};
template <typename T>
struct is_vector< T,
typename enable_if<
is_same<T,
std::vector< typename T::value_type,
typename T::allocator_type >
>::value
>::type
>
{
static const bool value = true;
};
... But I am not really sure of whether that is simpler or not.
In C++11 you can use type aliases (I think, untested):
template <typename T>
using is_vector = is_same<T, std::vector< typename T::value_type,
typename T::allocator_type > >;
The problem with your approach is that the type U is non-deducible in the context where it is used.
Actually, after some trial and error I found it's quite simple:
template<class T>
struct is_vector<std::vector<T> > {
static bool const value = true;
};
I'd still like to know how to write a more general is_container. Do I have to list all types by hand?
While the other answers here that try to guess whether a class is a container or not might work for you, I would like to present you with the alternative of naming the type you want to return true for. You can use this to build arbitrary is_(something) traits types.
template<class T> struct is_container : public std::false_type {};
template<class T, class Alloc>
struct is_container<std::vector<T, Alloc>> : public std::true_type {};
template<class K, class T, class Comp, class Alloc>
struct is_container<std::map<K, T, Comp, Alloc>> : public std::true_type {};
And so on.
You will need to include <type_traits> and whatever classes you add to your rules.
Why not do something like this for is_container?
template <typename Container>
struct is_container : std::false_type { };
template <typename... Ts> struct is_container<std::list<Ts...> > : std::true_type { };
template <typename... Ts> struct is_container<std::vector<Ts...> > : std::true_type { };
// ...
That way users can add their own containers by partially-specializing. As for is_vector et-al, just use partial specialization as I did above, but limit it to only one container type, not many.
The way I like to detect whether something is a container is to look for data() and size() member functions. Like this:
template <typename T, typename = void>
struct is_container : std::false_type {};
template <typename T>
struct is_container<T
, std::void_t<decltype(std::declval<T>().data())
, decltype(std::declval<T>().size())>> : std::true_type {};
template <typename T>
struct is_container {
template <
typename U,
typename I = typename U::const_iterator
>
static int8_t test(U* u);
template <typename U>
static int16_t test(...);
enum { value = sizeof test <typename std::remove_cv<T>::type> (0) == 1 };
};
template<typename T, size_t N>
struct is_container <std::array<T,N>> : std::true_type { };
Fast forward to 2018 and C++17, I was so daring to improve on #Frank answer
// clang++ prog.cc -Wall -Wextra -std=c++17
#include <iostream>
#include <vector>
namespace dbj {
template<class T>
struct is_vector {
using type = T ;
constexpr static bool value = false;
};
template<class T>
struct is_vector<std::vector<T>> {
using type = std::vector<T> ;
constexpr static bool value = true;
};
// and the two "olbigatory" aliases
template< typename T>
inline constexpr bool is_vector_v = is_vector<T>::value ;
template< typename T>
using is_vector_t = typename is_vector<T>::type ;
} // dbj
int main()
{
using namespace dbj;
std::cout << std::boolalpha;
std::cout << is_vector_v<std::vector<int>> << std::endl ;
std::cout << is_vector_v<int> << std::endl ;
} /* Created 2018 by dbj#dbj.org */
The "proof the pudding". There are better ways to do this, but this works for std::vector.
We can also use concepts. I compiled this with GCC 10.1 flag -std=c++20.
#include<concepts>
template<typename T>
concept is_container = requires (T a)
{
a.begin();
// Uncomment both lines for vectors only
// a.data(); // arrays and vectors
// a.reserve(1); // narrowed down to vectors
};
In our project we still didn't manage to migrate to compiler supporting C++11, so for type_traits of container objects I had to wrote a simple boost style helper:
template<typename Cont> struct is_std_container: boost::false_type {};
template<typename T, typename A>
struct is_std_container<std::vector<T,A> >: boost::true_type {};
template<typename T, typename A>
struct is_std_container<std::list<T,A> >: boost::true_type {};
template<typename T, typename A>
struct is_std_container<std::deque<T,A> >: boost::true_type {};
template<typename K, typename C, typename A>
struct is_std_container<std::set<K,C,A> >: boost::true_type {};
template<typename K, typename T, typename C, typename A>
struct is_std_container<std::map<K,T,C,A> >: boost::true_type {};
template<typename Cont> struct is_std_sequence: boost::false_type {};
template<typename T, typename A>
struct is_std_sequence<std::vector<T,A> >: boost::true_type {};
template<typename T, typename A>
struct is_std_sequence<std::list<T,A> >: boost::true_type {};
template<typename T, typename A>
struct is_std_sequence<std::deque<T,A> >: boost::true_type {};
If you also want to make it work for const std::vector, you can use the following:
namespace local {
template<typename T, typename _ = void>
struct isVector: std::false_type {
};
template<typename T>
struct isVector<T,
typename std::enable_if<
std::is_same<typename std::decay<T>::type, std::vector<typename std::decay<T>::type::value_type, typename std::decay<T>::type::allocator_type> >::value>::type> : std::true_type {
};
}
TEST(TypeTraitTest, testIsVector) {
ASSERT_TRUE(local::isVector<std::vector<int>>::value);
ASSERT_TRUE(local::isVector<const std::vector<int>>::value);
ASSERT_FALSE(local::isVector<std::list<int>>::value);
ASSERT_FALSE(local::isVector<int>::value);
std::vector<uint8_t> output;
std::vector<uint8_t> &output2 = output;
EXPECT_TRUE(core::isVector<decltype(output)>::value);
EXPECT_TRUE(core::isVector<decltype(output2)>::value);
}
Without the std::remove_cv call the second ASSERT_TRUE would fail. But of course this depends on your needs. The thing here is that according to the specs, std::is_same checks for const and volatile to also match.
The question inspired by recently arised question about extended std::is_base_of type trait.
Is there any technique, which allows us to distinguish between ordinary template parameter and template template parameter in modern C++ or its extensions (say, -std=gnu++1z clang++/g++)?
namespace details
{
template< /* ??? */ base >
struct is_derived_from;
template< typaneme base >
struct is_derived_from< base >
{
static std::true_type test(base *);
static std::false_type test(void *);
};
template< template< typename ...formal > base >
struct is_derived_from< /* ??? */ >
{
template< typename ...actual > // actual parameters must be here!
static std::true_type test(base< actual... > *);
static std::false_type test(void *);
};
} // namespace details
template< typename derived, /* ??? */ base >
using is_derived_from = decltype(details::is_derived_from< /* ? base< ? > */ >::test(std::declval< typename std::remove_cv< derived >::type * >()));
In positive case it allows us to make some of useful type traits much more powerfull (for example, STL's std::is_base_of).
I think it requires a language feature as a "generalized typenames", isn't it?
There can be only one set of template parameters for class templates, but you can use overloading constexpr function templates instead that dispatches to the appropriate class template. Take the is_derived_from trait in the linked question, with an extra SFINAE parameter so that you don't get a hard error when B is an inaccessible or ambiguous base:
#include <type_traits>
namespace detail
{
template <template <class...> class B, typename Derived>
struct is_derived_from
{
using U = typename std::remove_cv<Derived>::type;
template <typename... Args,
typename = std::enable_if_t<
std::is_convertible<U*, Base<Args...>*>::value>>
static auto test(B<Args...>*)
-> typename std::integral_constant<bool
, !std::is_same<U, B<Args...>>::value>;
static std::false_type test(void*);
using type = decltype(test(std::declval<U*>()));
};
using std::is_base_of; // may want to use is_convertible instead to match
// the semantics of is_derived_from
}
template <template <class...> class B, typename Derived>
constexpr bool my_is_base_of() { return detail::is_derived_from<B, Derived>::type::value; }
template <class B, typename Derived>
constexpr bool my_is_base_of() { return detail::is_base_of<B,Derived>::value; }
struct B {};
struct D : B {};
template<class ...>
struct B2 {};
struct D2 : B2<int, double> { };
int main() {
static_assert(my_is_base_of<B2, D2>(), "Oops");
static_assert(my_is_base_of<B, D>(), "Oops");
static_assert(my_is_base_of<B2<int, double>, D2>(), "Oops");
static_assert(!my_is_base_of<B, D2>(), "Oops");
}
Demo.
You asked:
Is there any technique, which allows us to distinct between ordinary template parameter and template template parameter in modern C++ or its extensions (say, -std=gnu++1z clang++/g++)?
Seems to me like you need something like:
template <typename T>
struct is_template_template : public std::false_type
{
};
template <typename T1, template <typename T> class T2>
struct is_template_template<T2<T1>> : std::true_type
{
};
Example Program
#include <iostream>
template <typename T>
struct is_template_template : public std::false_type
{
};
template <typename T1, template <typename T> class T2>
struct is_template_template<T2<T1>> : std::true_type
{
};
template <typename T> struct A {};
struct B {};
int main()
{
std::cout << std::boolalpha;
std::cout << is_template_template<A<int>>::value << std::endl;
std::cout << is_template_template<B>::value << std::endl;
return 0;
}
Output:
true
false