I learned SFINAE can be used to determine whether a member function exists in a class or not. For example, the following code can be used to check if the method hello is present in a class.
struct has_method_hello {
using yes = char[1];
using no = char[2];
template <typename U, typename C>
static constexpr yes& test(decltype(&U::hello));
template <typename>
static constexpr no& test(...);
static constexpr bool value = (sizeof(yes) == sizeof(test<T>(nullptr)));
};
struct Foo {
void hello() {}
}
std::cout << has_method_hello <Foo> :: value << std::endl; // 1
However, suppose the hello is templated, how can I modify the trick so it can still function properly?
struct Foo {
template <typename T>
void hello(T&) {...}
}
From here:
namespace details {
template<template<class...>class Z, class, class...>
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...>;
now you want to know if foo.hello(int&) can be called:
We have hello_r that gives you the return type of invoking .hello:
template<class F, class...Ts>
using hello_r = decltype(std::declval<F>().hello( std::declval<Ts>()... ));
which leads to can_hello:
template<class F, class...Ts>
using can_hello = can_apply<hello_r, F, Ts...>;
now
struct Foo {
template <typename T>
void hello(T&) {...}
};
int main() {
std::cout << can_hello<Foo&, int&>::value << '\n';
std::cout << can_hello<Foo&, char&>::value << '\n';
std::cout << can_hello<Foo&>::value << '\n';
}
prints 110.
live example.
First of all, showing you a shortened version of your original code:
template <typename T>
struct has_method_hello {
static constexpr auto test(int) -> decltype(std::declval<T&>().hello(), std::true_type());
static constexpr std::false_type test(...);
using result_type = decltype(test(0));
static const bool value = result_type::value;
};
struct Foo {
void hello() {}
};
Now making it work for a template parameter, easy, an example:
template <typename T>
struct has_method_hello {
static constexpr auto test(int) -> decltype(std::declval<T&>().hello(std::declval<int&>()), std::true_type());
static constexpr std::false_type test(...);
using result_type = decltype(test(0));
static const bool value = result_type::value;
};
struct Foo {
template <typename T>
void hello(T& v) {}
};
Note that, I have hardcoded int type here. You can make that part of has_method_hello template too.
This can be done:
// Example program
#include <iostream>
#include <string>
namespace mpl {
template<typename ...>
struct void_type
{
using type = void;
};
template<typename ...T>
using void_t = typename void_type<T...>::type;
} // namespace mpl
#define CAN_CALL_METHOD(NAME) \
namespace internal { \
template<typename T, typename ...Args> \
using result_of_call_method_##NAME = decltype( \
std::declval<T>().NAME(std::declval<Args>()...)); \
} \
template<typename T, typename Signature, typename = void> \
struct can_call_method_##NAME: std::false_type \
{}; \
template<typename T, typename ...Args> \
struct can_call_method_##NAME<T, void(Args...), \
mpl::void_t<internal::result_of_call_method_##NAME<T, Args...>> \
>: std::true_type \
{}; \
template<typename T, typename R, typename ...Args> \
struct can_call_method_##NAME<T, R(Args...), \
typename std::enable_if<!std::is_void<R>::value && \
std::is_convertible<internal::result_of_call_method_##NAME<T, Args...>, R \
>::value \
>::type \
>: std::true_type \
{};
CAN_CALL_METHOD(hello);
struct Foo {
template <typename T>
void hello(T&) {}
};
struct Foo1 {
};
int main()
{
std::cout << std::boolalpha;
std::cout << can_call_method_hello<Foo, void(int&)>::value << std::endl;
std::cout << can_call_method_hello<Foo1, void(int&)>::value << std::endl;
}
IdeOne link
This should work i hope for any method: templated, overloaded etc.
Related
In c++ i want to get the type of the arguments of a function.
The issue is i don't want to get the type for all of the arguments only the ones after the first one
template <typename T>
struct FuncTraits : FuncTraits<decltype(&T::operator())> {};
template <typename C, typename R, typename... Args>
struct FuncTraits<R(C::*)(Args...) const> : FuncTraits<void(*)(Args...)> {};
template <typename... Args> struct FuncTraits<void(*)(Args...)> {
using ArgCount = std::integral_constant<std::size_t, sizeof...(Args)>;
using ArgsType = std::tuple<typename std::decay<Args>::type...>;
};
In this example it gets the type for all of the arguments, but i want something more like this
template <typename T>
struct FuncTraits : FuncTraits<decltype(&T::operator())> {};
template <typename C, typename R, typename... Args>
struct FuncTraits<R(C::*)(int, Args...) const> : FuncTraits<void(*)(int unused, Args...)> {};
template <typename... Args> struct FuncTraits<void(*)(int unused, Args...)> {
using ArgCount = std::integral_constant<std::size_t, sizeof...(Args)>;
using ArgsType = std::tuple<typename std::decay<Args>::type...>;
};
Yet this fails complete to compile.
How do i achieve something like this?
You can add another template type to the base overload of FuncTraits:
#include <iostream>
#include <typeinfo>
struct foo
{
float operator ()(int a, char b, unsigned int c) const { return 0; }
};
template <typename T>
struct FuncTraits : FuncTraits<decltype(&T::operator())> {};
template <typename C, typename R, typename... Args>
struct FuncTraits<R(C::*)(Args...) const> : FuncTraits<void(*)(Args...)> {};
template <typename Arg, typename... Args> struct FuncTraits<void(*)(Arg, Args...)> {
using ArgCount = std::integral_constant<std::size_t, sizeof...(Args)>;
using ArgsType = std::tuple<typename std::decay<Args>::type...>;
};
int main()
{
using FooTraits = FuncTraits<foo>;
std::cout << FooTraits::ArgCount() << '\n'; \\ prints '2'
std::cout << typeid(FooTraits::ArgsType).name() << '\n'; \\ prints 'class std::tuple<char,unsigned int>'
}
Lets say I have
struct foo {
void ham() {}
void ham() const {}
};
struct bar {
void ham() {}
};
Assuming I have a templated function, can I tell whether given type has a const overload for ham?
With
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature) \
template <typename U> \
class traitsName \
{ \
private: \
template<typename T, T> struct helper; \
template<typename T> \
static std::uint8_t check(helper<signature, &funcName>*); \
template<typename T> static std::uint16_t check(...); \
public: \
static \
constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
}
DEFINE_HAS_SIGNATURE(has_ham_const, T::ham, void (T::*)() const);
And then
static_assert(has_ham_const<foo>::value, "unexpected");
static_assert(!has_ham_const<bar>::value, "unexpected");
Demo
SFINAE over and over again. Here is another option which is unspecified about the return types but lets you specify the arguments.
(For comparison: the approach by #Jarod42 checks the exact signature, return type + arguments, the other void_t expression sfinae stuff up to now checks only whether ham() can be called.)
Plus, it works with the current MSVC 2015 Update 1 version (unlike the usual void_t stuff).
template<typename V, typename ... Args>
struct is_callable_impl
{
template<typename C> static constexpr auto test(int) -> decltype(std::declval<C>().ham(std::declval<Args>() ...), bool{}) { return true; }
template<typename> static constexpr auto test(...) { return false; }
static constexpr bool value = test<V>(int{});
using type = std::integral_constant<bool, value>;
};
template<typename ... Args>
using is_callable = typename is_callable_impl<Args...>::type;
Use it as
struct foo
{
void ham() {}
void ham() const {}
int ham(int) const {}
};
int main()
{
std::cout
<<is_callable<foo>::value //true
<<is_callable<const foo>::value //true
<<is_callable<const foo, int>::value //true
<<is_callable<const foo, double>::value //also true, double is converted to int
<<is_callable<const foo, std::string>::value //false, can't call foo::ham(std::string) const
<<std::endl;
}
Demo on Coliru
For the "newest" sfinae stuff, however, I suggest you have a look at boost.hana.
Detector (like is_detected):
template <typename...>
using void_t = void;
template <typename T, template <typename> class D, typename = void>
struct detect : std::false_type {};
template <typename T, template <typename> class D>
struct detect<T, D, void_t<D<T>>> : std::true_type {};
Sample member verifier:
template <typename T>
using const_ham = decltype(std::declval<const T&>().ham());
Test:
static_assert(detect<foo, const_ham>::value, "!");
static_assert(!detect<bar, const_ham>::value, "!");
DEMO
Another option is to simulate void_t(to appear officially in C++17), which makes use of expression SFINAE to make sure your function is call-able on a const instance, regardless of its return type.
#include <iostream>
#include <type_traits>
struct Foo
{
void ham() const;
void ham();
};
struct Bar {
void ham() {}
};
template<typename...>
using void_t = void;
template<typename C, typename = void>
struct has_const_ham: std::false_type{};
template<typename C> // specialization, instantiated when there is ham() const
struct has_const_ham<C, void_t<decltype(std::declval<const C&>().ham())>> :
std::true_type{};
int main()
{
std::cout << std::boolalpha;
std::cout << has_const_ham<Foo>::value << std::endl;
std::cout << has_const_ham<Bar>::value << std::endl;
}
EDIT
If you want to enforce the return type, then derive the specialization from std::is_same, like
template<typename C> // specialization, instantiated when there is ham() const
struct has_const_ham<C, void_t<decltype(std::declval<const C&>().ham())>> :
std::is_same<decltype(std::declval<const C&>().ham()), void> // return must be void
{};
Live on Coliru
Here is a solution without macros which also does not care about return types:
template <typename T>
struct is_well_formed : std::true_type
{
};
template <typename T, typename = void>
struct has_const_ham : std::false_type
{
};
template <typename T>
struct has_const_ham<T,
typename std::enable_if<is_well_formed<decltype(
std::declval<const T&>().ham())>::value>::type>
: std::true_type
{
};
static_assert(has_const_ham<foo>::value, "oops foo");
static_assert(!has_const_ham<bar>::value, "oops bar");
I need to find out if a give type has function X as a callable function with a given parameter list. The check should not care about the return value however.
I found this solution from another Stack Overflow question which seems to work well. What it does is this:
#include <type_traits>
template <typename C, typename F, typename = void>
struct is_call_possible : public std::false_type {};
template <typename C, typename R, typename... A>
struct is_call_possible<C, R(A...),
typename std::enable_if<
std::is_same<R, void>::value ||
std::is_convertible<decltype(
std::declval<C>().operator()(std::declval<A>()...)
), R>::value
>::type
> : public std::true_type {};
This is exactly what I want except that in the check you also supply the desired return type. I was trying to find out a way to modify this to be able to check without taking the return type into account but I couldn't figure out a way.
Does anyone know how to do this?
Just do expression SFINAE and discard the result:
template <typename C, typename... Args>
struct is_call_possible {
private:
template<typename T>
static auto check(int)
-> decltype( std::declval<T>().operator()(std::declval<Args>()...),
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// overload is removed if this expression is ill-formed
std::true_type() );
template<typename>
static std::false_type check(...);
public:
static constexpr bool value = decltype(check<C>(0))::value;
};
Live example.
You might use:
#include <iostream>
namespace Detail {
struct is_callable
{
template<typename F, typename... A>
static decltype(std::declval<F>()(std::declval<A>()...), std::true_type())
test(int);
template<typename F, typename... A>
static std::false_type
test(...);
};
} // namespace Detai
template<typename F, typename... A>
using is_callable = decltype(Detail::is_callable::test<F, A...>(0));
struct X {
int operator ()(int) { return 0; }
};
int main() {
std::cout << is_callable<X>() << '\n';
std::cout << is_callable<X, int>() << '\n';
}
I want to make a std::function like object that can handle storing more than one overload.
Syntax sort of like this: my_function< int(double, int), double(double, double), char(int, int) >.
Or, more explicitly:
template<typename... Ts>
struct type_list {};
template<typename... Signatures >
struct my_function {
std::tuple< std::function<Signatures>... > m_functions;
typedef type_list< Signatures... > sig_list;
template<typename... Args>
typename pick_overload_signature< sig_list, type_list<Args...> >::return_value
operator()( Args&&... args )
{
return get<pick_overload_signature< sig_list, type_list<Args...> >::index>(m_functions)(std::forward<Args>(args)...);
}
};
My question: how should I write pick_overload_signatures?
Here is the work I've done on it:
My inclination would be to write a partial order on function signatures with respect to a given set of arguments, then sort the type list of function signatures, then grab the best (with possibly a compile-time assert that the best one is unique). To pull that off, I'd have to have a solid partial order (with respect to a set of arguments passed in) on function signatures...
13.3.3.1 tells me how to determine if there is a valid conversion. I can cheat for this by using the compiler to do a conversion for me, and use SFINAE to detect if it occurred for a given argument passed in and the signature of one of the "overloads".
13.3.3.2 tells me how to order these conversions. Here I have to detect if a conversion sequence is user defined or a standard sequence. I am not sure how to distinguish between the two.
Maybe I can use traits class to detect the existence of user-defined conversions sequences. Check for the existence of &S::operator D() and &D::D(S const&) and &D::D(S) and &D::D(S&&) or something like that.
has_user_defined_conversion<S,D>::value, has_standard_conversion<S,D>::value, etc?
Will this approach work, has someone already done it, or has someone already done parts of this?
Result of Answers
#include <type_traits>
#include <cstddef>
#include <utility>
#include <functional>
#include <tuple>
#include <string>
// Packaged list of types:
template<typename... Ts>
struct type_list {
template<template<typename...>class target>
struct apply {
typedef target<Ts...> type;
};
template<typename T>
struct append {
typedef type_list< Ts..., T > type;
};
template<typename T>
struct prepend {
typedef type_list< T, Ts... > type;
};
};
template<template<typename>class mapper, typename list>
struct map_types {
typedef type_list<> type;
};
template<template<typename>class mapper, typename T0, typename... Ts>
struct map_types<mapper, type_list<T0, Ts...>> {
typedef typename map_types<mapper, type_list<Ts...>>::type tail;
typedef typename tail::template prepend< typename mapper<T0>::type >::type type;
};
template<template<typename>class mapper, typename list>
using MapTypes = typename map_types<mapper, list>::type;
template<template<typename>class temp>
struct apply_template_to {
template<typename T>
struct action {
typedef temp<T> type;
};
};
template<template<typename> class temp, typename list>
struct apply_to_each:map_types< apply_template_to<temp>::template action, list > {};
template<template<typename> class temp, typename list>
using ApplyToEach = typename apply_to_each<temp, list>::type;
template<std::size_t n, typename list>
struct nth_type {};
template<std::size_t n, typename first, typename... elements>
struct nth_type<n, type_list<first, elements...>>:nth_type<n-1, type_list<elements...>>
{};
template<typename first, typename... elements>
struct nth_type<0, type_list<first, elements...>>
{
typedef first type;
};
template<std::size_t n, typename list>
using NthType = typename nth_type<n, list>::type;
// func data
template<typename R, typename... Args>
struct unpacked_func {
typedef R result_type;
typedef type_list<Args...> args_type;
typedef unpacked_func< R, Args... > unpacked_type;
template<template<typename>class target>
struct apply {
typedef target<R(Args...)> type;
};
};
namespace unpack_details {
// Extracting basic function properties:
template<typename Func>
struct unpack_func {};
template<typename R, typename... Args>
struct unpack_func< R(Args...) > {
typedef unpacked_func< R, Args... > type;
};
template<typename R, typename... Args>
struct unpack_func< unpacked_func<R, Args...> >:
unpack_func< R(Args...) >
{};
}
template<typename Func>
using FuncUnpack = typename unpack_details::unpack_func<Func>::type;
template<typename Func>
struct func_props:func_props<FuncUnpack<Func>> {};
template<typename R, typename... Args>
struct func_props<unpacked_func<R, Args...>>:
unpacked_func<R, Args...>
{};
template<typename Func>
using FuncResult = typename func_props<Func>::result_type;
template<typename Func>
using FuncArgs = typename func_props<Func>::args_type;
template<typename Func>
struct make_func_ptr:make_func_ptr<FuncUnpack<Func>> {};
template<typename R, typename... Args>
struct make_func_ptr< unpacked_func< R, Args... > > {
typedef R(*type)(Args...);
};
template<typename Func>
using MakeFuncPtr = typename make_func_ptr<Func>::type;
// Marking a type up with an index:
template<typename R, std::size_t i>
struct indexed_type {
typedef R type;
enum { value = i };
};
// Sequences of size_t:
template<std::size_t... s>
struct seq {};
template<std::size_t min, std::size_t max, std::size_t... s>
struct make_seq: make_seq< min, max-1, max-1, s...> {};
template<std::size_t min, std::size_t... s>
struct make_seq< min, min, s...> {
typedef seq<s...> type;
};
template<std::size_t max, std::size_t min=0>
using MakeSeq = typename make_seq<max, min>::type;
namespace overload_details {
template<std::size_t n, typename... Overloads>
struct indexed_linear_signatures {};
template<typename Overload>
struct signature_generator {};
template<typename R, typename... Args>
struct signature_generator<unpacked_func<R, Args...>> {
R operator()(Args...); // no impl
};
template<typename Func, std::size_t i>
struct indexed_retval {};
template<typename R, typename... Args, std::size_t i>
struct indexed_retval< unpacked_func<R, Args...>, i > {
typedef unpacked_func<indexed_type<R,i>, Args...> type;
};
template<typename Func, std::size_t i>
using IndexRetval = typename indexed_retval<Func,i>::type;
void test1() {
typedef overload_details::IndexRetval< FuncUnpack<void()>, 0 > indexed;
indexed::apply<std::function>::type test = []()->indexed_type<void,0> {return indexed_type<void,0>();};
}
template<std::size_t n, typename Overload, typename... Overloads>
struct indexed_linear_signatures<n, Overload, Overloads...>:
signature_generator<IndexRetval<FuncUnpack<Overload>,n>>,
indexed_linear_signatures<n+1, Overloads...>
{};
template<typename T>
struct extract_index {};
template<typename T, std::size_t i>
struct extract_index<indexed_type<T,i>> {
enum {value = i};
};
template<typename T>
using Decay = typename std::decay<T>::type;
template<typename indexed_overloads, typename... Args>
struct get_overload_index {
enum{ value = extract_index< Decay<decltype( std::declval<indexed_overloads>()(std::declval<Args>()...) )> >::value };
};
template<typename Overloads, typename Args>
struct get_overload {};
template<typename... Overloads, typename... Args>
struct get_overload<type_list<Overloads...>, type_list<Args...>> {
typedef indexed_linear_signatures<0, Overloads...> sig_index;
enum { index = get_overload_index< sig_index, Args... >::value };
typedef FuncUnpack< NthType<index, type_list<Overloads...> > > unpacked_sig;
};
template<typename Overloads, typename Args>
using GetOverloadSig = typename get_overload< Overloads, Args >::unpacked_sig;
}
template<typename Overloads, typename Arguments>
struct pick_overload_signature {
enum{ index = overload_details::get_overload<Overloads, Arguments>::index };
typedef overload_details::GetOverloadSig<Overloads, Arguments> unpacked_sig;
};
#include <iostream>
void test1() {
typedef type_list< void(int), void(double) > overloads;
typedef type_list< int > args;
typedef pick_overload_signature< overloads, args > result;
std::cout << result::index << " should be 0\n";
typedef type_list< double > args2;
typedef pick_overload_signature< overloads, args2 > result2;
std::cout << result2::index << " should be 1\n";
// ;
typedef ApplyToEach< std::function, overloads >::apply< std::tuple >::type functions;
typedef std::tuple< std::function<void(int)>, std::function<void(double)> > functions0;
std::cout << std::is_same<functions, functions0>() << " should be true\n";
functions funcs{
[](int) { std::cout << "int!" << "\n"; },
[](double) { std::cout << "double!" << "\n"; }
};
std::get<result::index>(funcs)(0);
}
template< typename... Signatures >
struct my_function {
typedef type_list<Signatures...> signatures;
typedef std::tuple< std::function<Signatures>... > func_tuple;
func_tuple functions;
template<typename... Funcs>
explicit my_function(Funcs&&... funcs):
functions( std::forward<Funcs>(funcs)... )
{}
template<typename... Args>
auto
operator()(Args&&... args) const ->
typename overload_details::GetOverloadSig< signatures, type_list<Args...> >::result_type
{
return std::get<
pick_overload_signature< signatures, type_list<Args...> >::index
>(functions)(std::forward<Args>(args)...);
}
// copy/assign boilerplate
template<typename... OtherSignatures>
my_function( my_function<OtherSignatures...> const& o ):
functions( o.functions )
{}
template<typename... OtherSignatures>
my_function( my_function<OtherSignatures...> && o ):
functions( std::move(o.functions) )
{}
template<typename... OtherSignatures>
my_function& operator=( my_function<OtherSignatures...> const& o )
{
functions = o.functions;
return *this;
}
template<typename... OtherSignatures>
my_function& operator=( my_function<OtherSignatures...> && o ) {
functions = std::move(o.functions);
return *this;
}
};
struct printer {
template<typename T>
void operator()( T const& t ) {
std::cout << t << "\n";
}
};
void print(int x) {
std::cout << "int is " << x << "\n";
}
void print(std::string s) {
std::cout << "string is " << s << "\n";
}
void test2() {
my_function< void(int), void(std::string) > funcs{
[](int x){ std::cout << "int is " << x << "\n";},
[](std::string s){ std::cout << "string is " << s << "\n";}
};
std::cout << "test2\n";
funcs("hello");
funcs(0);
my_function< void(int), void(std::string) > funcs2{
printer(), printer()
};
funcs2("hello");
funcs2(12.7);
// doesn't work:
/*
my_function< void(int), void(std::string) > funcs3{
print,
print
};
*/
}
void test3() {
}
int main() {
test1();
test2();
test3();
}
Isn't done, but is usable.
Thanks all!
i'm sure it is doable your way, but may be you will be satisfied with this one https://gist.github.com/dabrahams/3779345
template<class...Fs> struct overloaded;
template<class F1, class...Fs>
struct overloaded<F1, Fs...> : F1, overloaded<Fs...>::type
{
typedef overloaded type;
overloaded(F1 head, Fs...tail)
: F1(head),
overloaded<Fs...>::type(tail...)
{}
using F1::operator();
using overloaded<Fs...>::type::operator();
};
template<class F>
struct overloaded<F> : F
{
typedef F type;
using F::operator();
};
template<class...Fs>
typename overloaded<Fs...>::type overload(Fs...x)
{ return overloaded<Fs...>(x...); }
auto f = overload(
[](int x) { return x+1; },
[](char const* y) { return y + 1; },
[](int* y) { return y; });
I think you can use something like these traits... But if you want make overloading resolution fully as in standard - you need more code http://en.cppreference.com/w/cpp/language/implicit_cast
#include <type_traits>
template<typename T, typename D>
struct is_constructible
{
template<typename C, typename F>
static auto test(C*) -> decltype(C(std::declval<F>()), std::true_type());
template<typename, typename>
static std::false_type test(...);
static const bool value = std::is_class<T>::value &&
std::is_same<std::true_type, decltype(test<T, D>(0))>::value;
};
template<typename T, typename D>
struct has_conversion_operator
{
static std::true_type test(D d);
template<typename C, typename F>
static auto test(C* c) -> decltype(test(*c));
template<typename, typename>
static std::false_type test(...);
static const bool value = std::is_class<T>::value &&
!is_constructible<T, D>::value &&
std::is_same<std::true_type, decltype(test<T, D>(0))>::value;
};
template<typename T, typename D>
struct is_standard_convertible :
std::integral_constant<bool, !has_conversion_operator<T, D>::value &&
!is_constructible<T, D>::value &&
std::is_convertible<T, D>::value>
{
};
template<typename T, typename D>
struct is_user_convertible :
std::integral_constant<bool, has_conversion_operator<T, D>::value ||
is_constructible<T, D>::value>
{
};
and implement what you want like:
first check, that signatures are standard_convertible
if not check that signature are user_convertible.
I would like to write a template that will determine if a type is an stl container at compile time.
I've got the following bit of code:
struct is_cont{};
struct not_cont{};
template <typename T>
struct is_cont { typedef not_cont result_t; };
but I'm not sure how to create the necessary specializations for std::vector<T,Alloc>, deque<T,Alloc>, set<T,Alloc,Comp> etc...
Note: the following code is taken from an excellent utility called pretty-print written by #Kerrek SB (a topic on it at stackoverflow).
Disclaimer : I don't know if I'm allowed to copy and paste this code here without taking permission from the original author. #Kerrek, let me know if you've any issue. :-)
You can use this classs template:
template<typename T>
struct is_container : std::integral_constant<bool, has_const_iterator<T>::value && has_begin_end<T>::beg_value && has_begin_end<T>::end_value>
{ };
Usage:
std::cout << is_container<std::vector<int>>::value << std::endl; //true
std::cout << is_container<std::list<int>>::value << std::endl; //true
std::cout << is_container<std::map<int>>::value << std::endl; //true
std::cout << is_container<std::set<int>>::value << std::endl; //true
std::cout << is_container<int>::value << std::endl; //false
Note that is_container needs following helper class templates:
template<typename T>
struct has_const_iterator
{
private:
typedef char yes;
typedef struct { char array[2]; } no;
template<typename C> static yes test(typename C::const_iterator*);
template<typename C> static no test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
typedef T type;
};
template <typename T>
struct has_begin_end
{
template<typename C> static char (&f(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::begin)),
typename C::const_iterator(C::*)() const>::value, void>::type*))[1];
template<typename C> static char (&f(...))[2];
template<typename C> static char (&g(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::end)),
typename C::const_iterator(C::*)() const>::value, void>::type*))[1];
template<typename C> static char (&g(...))[2];
static bool const beg_value = sizeof(f<T>(0)) == 1;
static bool const end_value = sizeof(g<T>(0)) == 1;
};
First, you define your primary template, which will have a member which is false in the default case:
template <typename T>
struct is_cont {
static const bool value = false;
};
Then you will define partial specializations for your container types which have a value of true instead:
template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
static const bool value = true;
};
Then for a type X that you want to check, use it like
if (is_cont<X>::value) { ... }
Many of the already proposed solutions are verbose for detecting STL containers.
They focus on the characteristics that all containers possess, instead of explicitly stating what the containers are.
If you wanted to create your own containers and have them evaluated with a true type, I'd recommend the other solutions. If you only want to validate legitimate STL containers, and not STL-like containers, consider using the following implementation, as it provides precise STL container detection:
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <type_traits>
//specialize a type for all of the STL containers.
namespace is_stl_container_impl{
template <typename T> struct is_stl_container:std::false_type{};
template <typename T, std::size_t N> struct is_stl_container<std::array <T,N>> :std::true_type{};
template <typename... Args> struct is_stl_container<std::vector <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::deque <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::list <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::forward_list <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::set <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::multiset <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::map <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::multimap <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::unordered_set <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::unordered_multiset<Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::unordered_map <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::unordered_multimap<Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::stack <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::queue <Args...>>:std::true_type{};
template <typename... Args> struct is_stl_container<std::priority_queue <Args...>>:std::true_type{};
}
//type trait to utilize the implementation type traits as well as decay the type
template <typename T> struct is_stl_container {
static constexpr bool const value = is_stl_container_impl::is_stl_container<std::decay_t<T>>::value;
};
Note the use of std::decay to avoid incorrect type deduction based on type qualifiers. Also, we've utilized inheriting std::true_type and std::false_type to avoid specifying the ::type types ourselves. C++11 variadic templates are used to deduce the n amount of template type parameters needed to construct the containers.
Using the implementation is as you would expect:
std::cout << std::boolalpha;
std::cout << is_stl_container<std::vector<int>>::value << '\n';
std::cout << is_stl_container<std::vector<int>const&>::value << '\n';
std::cout << is_stl_container<int>::value << '\n';
prints:
true
true
false
Pursuing the suggestion that a generic compiletime test for
has-an-stl-container-like-interface would be an appropriate
solution, this one defines an stl-like container T
by the interface:
T::iterator T::begin();
T::iterator T::end();
T::const_iterator T::begin() const;
T::const_iterator T::end() const;
*T::iterator is T::value_type &
*T::const_iterator is T::value_type const &
Additional requirements, e.g. a size() method, could be added in an obvious
fashion, or other canonical type interfaces probed at compiletime in an obvious
similar way.
#ifndef IS_STL_CONTAINER_LIKE_H
#define IS_STL_CONTAINER_LIKE_H
#include <type_traits>
template<typename T>
struct is_stl_container_like
{
typedef typename std::remove_const<T>::type test_type;
template<typename A>
static constexpr bool test(
A * pt,
A const * cpt = nullptr,
decltype(pt->begin()) * = nullptr,
decltype(pt->end()) * = nullptr,
decltype(cpt->begin()) * = nullptr,
decltype(cpt->end()) * = nullptr,
typename A::iterator * pi = nullptr,
typename A::const_iterator * pci = nullptr,
typename A::value_type * pv = nullptr) {
typedef typename A::iterator iterator;
typedef typename A::const_iterator const_iterator;
typedef typename A::value_type value_type;
return std::is_same<decltype(pt->begin()),iterator>::value &&
std::is_same<decltype(pt->end()),iterator>::value &&
std::is_same<decltype(cpt->begin()),const_iterator>::value &&
std::is_same<decltype(cpt->end()),const_iterator>::value &&
std::is_same<decltype(**pi),value_type &>::value &&
std::is_same<decltype(**pci),value_type const &>::value;
}
template<typename A>
static constexpr bool test(...) {
return false;
}
static const bool value = test<test_type>(nullptr);
};
#endif
Here is a test program, built with GCC 4.7.2, clang 3.2, Intel C++ 13.1.1:
#include "is_stl_container_like.h"
// Testing ...
#include <iostream>
#include <vector>
#include <array>
#include <functional>
using namespace std;
template<class C>
struct polymorphic : private C
{
typedef typename C::value_type value_type;
typedef typename C::iterator iterator;
typedef typename C::const_iterator const_iterator;
virtual ~polymorphic(){}
virtual const_iterator begin() const {
return C::begin();
}
virtual iterator begin() {
return C::begin();
}
virtual const_iterator end() const {
return C::end();
}
virtual iterator end() {
return C::end();
}
};
template<class C>
struct reject : private C
{
typedef typename C::value_type value_type;
typedef typename C::iterator iterator;
typedef typename C::const_iterator const_iterator;
const_iterator begin() {
return C::begin();
}
iterator begin() const {
return C::begin();
}
const_iterator end() {
return C::end();
}
iterator end() const {
return C::end();
}
};
int main()
{
cout << is_stl_container_like<vector<int> const >::value << endl; // Yes
cout << is_stl_container_like<array<int,42>>::value << endl; // Yes
cout << is_stl_container_like<polymorphic<vector<int>>>::value << endl; // Yes
cout << is_stl_container_like<function<int(int)>>::value << endl; // No
cout << is_stl_container_like<int>::value << endl; // No
cout << is_stl_container_like<reject<vector<int>>>::value << endl; //No
}
In C++20, you might use concept,
up to you to add more checks from what you consider as container, but it might look like:
template <typename T>
concept Container = requires(T t)
{
std::begin(t);
std::end(t);
};
Usage example.
There are already existing concepts instandard which might interest you as:
std::ranges::range
There is is_container in boost
http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/advanced/customize/is_container.html
is_container<C>::type ---
Result of the metafunction that evaluates to mpl::true_ if a given type, C, is to be treated as a container, mpl::false_ otherwise Generally, any implementation of is_container needs to behave as if if was a MPL Boolean Constant..
Tested with MSVC 2019:
template<class C>
struct IsContainer {
private:
template<class D>
static constexpr auto hasValueType() -> decltype(typename D::value_type(), std::true_type()) {
return {};
}
template<class D>
static constexpr auto hasIteratorAlias() -> decltype(typename D::iterator(), std::true_type()) {
return {};
}
template<class D>
static constexpr std::false_type hasIteratorAlias(...) {
return {};
}
template<class D>
static constexpr auto hasConstIteratorAlias() -> decltype(typename D::const_iterator(), std::true_type()) {
return {};
}
template<class D>
static constexpr auto hasBegin() -> decltype(decltype(std::begin(std::declval<D>())){}, std::true_type()) {
return {};
}
template<class D>
static constexpr std::false_type hasBegin(...) {
return {};
}
template<class D>
static constexpr auto hasEnd() -> decltype(decltype(std::end(std::declval<D>())){}, std::true_type()) {
return {};
}
template<class D>
static constexpr std::false_type hasEnd(...) {
return {};
}
template<class D>
static constexpr std::false_type hasConstIteratorAlias(...) {
return {};
}
template<class D>
static constexpr std::false_type hasValueType(...) {
return {};
}
public:
constexpr static bool value = hasValueType<C>().value &&
hasIteratorAlias<C>().value &&
hasConstIteratorAlias<C>().value &&
hasBegin<C>().value &&
hasEnd<C>().value;
constexpr bool operator()() const {
return value;
}
};
Usage:
std::vector<int> vec;
int x = 0;
float y = 0.f;
std::array<int, 1> arr{};
constexpr auto val = IsContainer<decltype(vec)>()();
constexpr auto val2 = IsContainer<decltype(x)>()();
constexpr auto val3 = IsContainer<decltype(y)>()();
constexpr auto val4 = IsContainer<decltype(arr)>()();
std::cout << static_cast<bool>(val) << '\n';
std::cout << static_cast<bool>(val2) << '\n';
std::cout << static_cast<bool>(val3) << '\n';
std::cout << static_cast<bool>(val4) << '\n';
Output:
1
0
0
1
This code defines traits for container. It's originally from prettyprint library:
//put this in type_utils.hpp
#ifndef commn_utils_type_utils_hpp
#define commn_utils_type_utils_hpp
#include <type_traits>
#include <valarray>
namespace common_utils { namespace type_utils {
//from: https://raw.githubusercontent.com/louisdx/cxx-prettyprint/master/prettyprint.hpp
//also see https://gist.github.com/louisdx/1076849
namespace detail
{
// SFINAE type trait to detect whether T::const_iterator exists.
struct sfinae_base
{
using yes = char;
using no = yes[2];
};
template <typename T>
struct has_const_iterator : private sfinae_base
{
private:
template <typename C> static yes & test(typename C::const_iterator*);
template <typename C> static no & test(...);
public:
static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
using type = T;
void dummy(); //for GCC to supress -Wctor-dtor-privacy
};
template <typename T>
struct has_begin_end : private sfinae_base
{
private:
template <typename C>
static yes & f(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)),
typename C::const_iterator(C::*)() const>::value>::type *);
template <typename C> static no & f(...);
template <typename C>
static yes & g(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)),
typename C::const_iterator(C::*)() const>::value, void>::type*);
template <typename C> static no & g(...);
public:
static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes);
void dummy(); //for GCC to supress -Wctor-dtor-privacy
};
} // namespace detail
// Basic is_container template; specialize to derive from std::true_type for all desired container types
template <typename T>
struct is_container : public std::integral_constant<bool,
detail::has_const_iterator<T>::value &&
detail::has_begin_end<T>::beg_value &&
detail::has_begin_end<T>::end_value> { };
template <typename T, std::size_t N>
struct is_container<T[N]> : std::true_type { };
template <std::size_t N>
struct is_container<char[N]> : std::false_type { };
template <typename T>
struct is_container<std::valarray<T>> : std::true_type { };
template <typename T1, typename T2>
struct is_container<std::pair<T1, T2>> : std::true_type { };
template <typename ...Args>
struct is_container<std::tuple<Args...>> : std::true_type { };
}} //namespace
#endif
For more explanation see my blog post.
Related question: c++ template class; function with arbitrary container type, how to define it?