Related
I need to know the exact number of arguments that a lambda has. I do not care for their types, I just need a count.
auto lambda0 = [&]() { ... };
auto lambda1 = [&](int32_t a) { ... };
auto lambda2 = [&](int32_t a, auto b) { ... };
lambda_details<decltype(lambda0)>::argument_count; // Equals 0
lambda_details<decltype(lambda1)>::argument_count; // Equals 1
lambda_details<decltype(lambda2)>::argument_count; // Equals 2
Detecting variadic lambdas would also be nice so that I can deal with that edge case as well.
auto lambda_variadic = [&](auto... args){ ... };
lambda_details<decltype(lambda_variadic)>::is_variadic; // Equals true
How can I get this information?
You can create an object that can go into any parameter by overloading conversion operator. From there just test if the lambda is callable with a given number of such arguments, counting down from some arbitrary large number. If the lambda happens to be callable on the first try (with given arbitrary large number of arguments), we can assume it is variadic:
#include <iostream>
#include <utility>
#include <type_traits>
struct any_argument {
template <typename T>
operator T&&() const;
};
template <typename Lambda, typename Is, typename = void>
struct can_accept_impl
: std::false_type
{};
template <typename Lambda, std::size_t ...Is>
struct can_accept_impl<Lambda, std::index_sequence<Is...>,
decltype(std::declval<Lambda>()(((void)Is, any_argument{})...), void())>
: std::true_type
{};
template <typename Lambda, std::size_t N>
struct can_accept
: can_accept_impl<Lambda, std::make_index_sequence<N>>
{};
template <typename Lambda, std::size_t Max, std::size_t N, typename = void>
struct lambda_details_impl
: lambda_details_impl<Lambda, Max, N - 1>
{};
template <typename Lambda, std::size_t Max, std::size_t N>
struct lambda_details_impl<Lambda, Max, N, std::enable_if_t<can_accept<Lambda, N>::value>>
{
static constexpr bool is_variadic = (N == Max);
static constexpr std::size_t argument_count = N;
};
template <typename Lambda, std::size_t Max = 50>
struct lambda_details
: lambda_details_impl<Lambda, Max, Max>
{};
int main()
{
auto lambda0 = []() {};
auto lambda1 = [](int a) {};
auto lambda2 = [](int a, auto b) {};
auto lambda3 = [](int a, auto b, char = 'a') {};
auto lambda4 = [](int a, auto b, char = 'a', auto...) {};
std::cout << lambda_details<decltype(lambda0)>::is_variadic << " " << lambda_details<decltype(lambda0)>::argument_count << "\n"; // 0 0
std::cout << lambda_details<decltype(lambda1)>::is_variadic << " " << lambda_details<decltype(lambda1)>::argument_count << "\n"; // 0 1
std::cout << lambda_details<decltype(lambda2)>::is_variadic << " " << lambda_details<decltype(lambda2)>::argument_count << "\n"; // 0 2
std::cout << lambda_details<decltype(lambda3)>::is_variadic << " " << lambda_details<decltype(lambda3)>::argument_count << "\n"; // 0 3
std::cout << lambda_details<decltype(lambda4)>::is_variadic << " " << lambda_details<decltype(lambda4)>::argument_count << "\n"; // 1 50
}
I have solved it using a modified version of #yuri kilochek's answer.
Instead of starting from 50 arguments and counting down, we start at zero and count up. When we get a match we know the minimum amount of arguments required to call the lambda. We then keep searching up until a sane maximum to see if there is a maximum amount of arguments (this can happen when you have default arguments).
If the argument count limit is reached, we assume the lambda to be variadic.
This implementation reduces the amount of template instantiations for non variadic lambdas significantly. It also gives us the minimum amount of arguments for all lambdas, and the maximum amount of arguments for any non-variadic lambdas.
Again, big thanks to Yuri Kilochek for laying the foundation for this elegant solution. Check his answer for more details about the implementation.
struct any_argument
{
template <typename T>
operator T && () const;
};
template <typename Lambda, typename Is, typename = void>
struct can_accept_impl : std::false_type
{};
template <typename Lambda, std::size_t ...Is>
struct can_accept_impl <Lambda, std::index_sequence<Is...>, decltype(std::declval<Lambda>()(((void)Is, any_argument{})...), void())> : std::true_type
{};
template <typename Lambda, std::size_t N>
struct can_accept : can_accept_impl<Lambda, std::make_index_sequence<N>>
{};
template <typename Lambda, std::size_t N, size_t Max, typename = void>
struct lambda_details_maximum
{
static constexpr size_t maximum_argument_count = N - 1;
static constexpr bool is_variadic = false;
};
template <typename Lambda, std::size_t N, size_t Max>
struct lambda_details_maximum<Lambda, N, Max, std::enable_if_t<can_accept<Lambda, N>::value && (N <= Max)>> : lambda_details_maximum<Lambda, N + 1, Max>
{};
template <typename Lambda, std::size_t N, size_t Max>
struct lambda_details_maximum<Lambda, N, Max, std::enable_if_t<can_accept<Lambda, N>::value && (N > Max)>>
{
static constexpr bool is_variadic = true;
};
template <typename Lambda, std::size_t N, size_t Max, typename = void>
struct lambda_details_minimum : lambda_details_minimum<Lambda, N + 1, Max>
{
static_assert(N <= Max, "Argument limit reached");
};
template <typename Lambda, std::size_t N, size_t Max>
struct lambda_details_minimum<Lambda, N, Max, std::enable_if_t<can_accept<Lambda, N>::value>> : lambda_details_maximum<Lambda, N, Max>
{
static constexpr size_t minimum_argument_count = N;
};
template <typename Lambda, size_t Max = 50>
struct lambda_details : lambda_details_minimum<Lambda, 0, Max>
{};
Another important thing to note is that any_argument doesn't automatically play nice with operators. You will have to overload every single one if you want it to work with auto arguments that are operated upon (e.g. [](auto a) { return a * 2; }). It will end up looking more like this:
struct any_argument
{
template <typename T> operator T && () const;
any_argument& operator ++();
any_argument& operator ++(int);
any_argument& operator --();
any_argument& operator --(int);
template <typename T> friend any_argument operator + (const any_argument&, const T&);
template <typename T> friend any_argument operator + (const T&, const any_argument&);
template <typename T> friend any_argument operator - (const any_argument&, const T&);
template <typename T> friend any_argument operator - (const T&, const any_argument&);
template <typename T> friend any_argument operator * (const any_argument&, const T&);
template <typename T> friend any_argument operator * (const T&, const any_argument&);
template <typename T> friend any_argument operator / (const any_argument&, const T&);
template <typename T> friend any_argument operator / (const T&, const any_argument&);
// And every other operator in existence
};
I don't know a way to count all argument of a generic-lambda [edit: but yuri kilochek know how to do it: see his answer for a great solution].
For non-generic lambdas, as suggested by Igor Tandetnik, you can detect the types (return and arguments) of the pointer to operator() and count the arguments.
Something as follows
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...) const)
{ return sizeof...(Args); }
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...))
{ return sizeof...(Args); }
template <typename L>
constexpr auto countArguments (L)
{ return cah(&L::operator()); }
But, unfortunately, this doesn't works when you introduce an auto argument because, with an auto argument, you transform operator() in a template function.
About detecting a variadic lambda, you can detect a function with only a variadic list of arguments (let me call it "pure variadic"), as your lambda_variadic, trying to call it with zero and with (by example) 50 argument of a given type.
I mean something as follows
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <typename T, std::size_t N>
using getType_t = typename getType<T, N>::type;
// isPureVariadic arguments helper
template <typename T>
constexpr std::false_type ipvh (...);
// isPureVariadic arguments helper
template <typename T, typename F, std::size_t ... Is>
constexpr auto ipvh (F f, std::index_sequence<Is...>)
-> decltype( f(std::declval<getType_t<T, Is>>()...), std::true_type{} );
template <typename F>
constexpr bool isPureVariadic (F f)
{ return
decltype(ipvh<int>(f, std::make_index_sequence<0u>{}))::value
&& decltype(ipvh<int>(f, std::make_index_sequence<50u>{}))::value; }
but this isn't perfect because gives false positives and false negatives.
A problem is that when you check it with a "not pure variadic lambda" as
auto lambda_variadic2 = [&](std::string, auto... args){ ... };
that is variadic but the first argument doesn't accept a int, isn't detected as "pure variadic"; unfortunately the following lambda
auto lambda_variadic3 = [&](long, auto... args){ ... };
is detected as "pure variadic" because the first argument accept a int.
To avoid this problem, you can modify the function to check the call with 50 arguments of two incompatible types; by example
template <typename F>
constexpr bool isPureVariadic (F f)
{ return
decltype(ipvh<int>(f, std::make_index_sequence<0u>{}))::value
&& decltype(ipvh<int>(f, std::make_index_sequence<50u>{}))::value
&& decltype(ipvh<std::string>(f, std::make_index_sequence<50u>{}))::value; }
Another problem is that are detected as "pure virtual" also non-variadic generic-lambda functions receiving a number of arguments higher that the checked number (50, in the example).
And remain the problem that this solution doesn't detect lambda_variadic2 (a non-pure variadic lambda) as variadic.
The following is a full compiling example with the best I can imagine about your question
#include <iostream>
#include <utility>
#include <type_traits>
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...) const)
{ return sizeof...(Args); }
// count arguments helper
template <typename R, typename T, typename ... Args>
constexpr std::size_t cah (R(T::*)(Args...))
{ return sizeof...(Args); }
template <typename L>
constexpr auto countArguments (L)
{ return cah(&L::operator()); }
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <typename T, std::size_t N>
using getType_t = typename getType<T, N>::type;
// isPureVariadic arguments helper
template <typename T>
constexpr std::false_type ipvh (...);
// isPureVariadic arguments helper
template <typename T, typename F, std::size_t ... Is>
constexpr auto ipvh (F f, std::index_sequence<Is...>)
-> decltype( f(std::declval<getType_t<T, Is>>()...), std::true_type{} );
template <typename F>
constexpr bool isPureVariadic (F f)
{ return
decltype(ipvh<int>(f, std::make_index_sequence<0u>{}))::value
&& decltype(ipvh<int>(f, std::make_index_sequence<50u>{}))::value; }
int main() {
auto lambda0 = [&]() {};
auto lambda1 = [&](int) {};
auto lambda2 = [&](int, auto) {};
auto lambda3 = [&](auto...) {};
std::cout << countArguments(lambda0) << std::endl;
std::cout << countArguments(lambda1) << std::endl;
// std::cout << countArguments(lambda2) << std::endl; // compilation error
// std::cout << countArguments(lambda3) << std::endl; // compilation error
std::cout << isPureVariadic(lambda0) << std::endl;
std::cout << isPureVariadic(lambda1) << std::endl;
std::cout << isPureVariadic(lambda2) << std::endl;
std::cout << isPureVariadic(lambda3) << std::endl;
}
I have a tuple of objects of different classes. I want to iterate over the tuple and call a certain method only if those class has one.
For example (pseudo-code):
struct A { int get( ) { return 5; }; };
struct B { };
struct C { int get( ) { return 10; }; };
int i = 0;
tuple<A, B, C> t;
for ( auto t_element : t )
{
if constexpr ( has_get_method( decltype(t_element) ) )
{
i += t_element.get( );
}
}
I already know how to iterate over the tuple and check if a class has some method using sfinae but how do I skip object that do not have the required method?
EDIT:
If you found this old question in the future, please know that this can be done much easier now using concepts from C++20 standard.
Just write a sfinae'd function and a catchall in case the previous one fails. You don't have to use if constexpr for that and you can't use it actually in C++14 (that is how you tagged the question).
Here is a minimal, working example:
#include <tuple>
#include <iostream>
auto value(...) { return 0; }
template <typename T>
auto value(T &t) -> decltype(t.get()) {
return t.get();
}
struct A { int get() { return 5; }; };
struct B {};
struct C { int get() { return 10; }; };
int main() {
int i = 0;
std::tuple<A, B, C> t;
i += value(std::get<0>(t));
i += value(std::get<1>(t));
i += value(std::get<2>(t));
std::cout << i << std::endl;
}
See it up and running on wandbox.
If you have any argument you want to use to test it, you can use std::forward as:
template <typename T, typename... Args>
auto value(T &t, Args&&... args)
-> decltype(t.get(std::forward<Args>(args)...))
{ return t.get(std::forward<Args>(args)...); }
Then invoke it as:
i += value(std::get<0>(t), params);
You can create a type-traits, to check if a class has a get() method, by declaring a couple of functions (no need to define them)
template <typename>
constexpr std::false_type withGetH (long);
template <typename T>
constexpr auto withGetH (int)
-> decltype( std::declval<T>().get(), std::true_type{} );
template <typename T>
using withGet = decltype( withGetH<T>(0) );
The following is a fully working c++17 example
#include <tuple>
#include <iostream>
#include <type_traits>
template <typename>
constexpr std::false_type withGetH (long);
template <typename T>
constexpr auto withGetH (int)
-> decltype( std::declval<T>().get(), std::true_type{} );
template <typename T>
using withGet = decltype( withGetH<T>(0) );
struct A { int get( ) { return 5; }; };
struct B { };
struct C { int get( ) { return 10; }; };
template <typename T>
int addGet (T & t)
{
int ret { 0 };
if constexpr ( withGet<T>{} )
ret += t.get();
return ret;
}
int main ()
{
int i = 0;
std::tuple<A, B, C> t;
i += addGet(std::get<0>(t));
i += addGet(std::get<1>(t));
i += addGet(std::get<2>(t));
std::cout << i << std::endl;
}
If you can't use if constexpr, you can write (in c++11/14) getAdd() as follows, using tag dispatching
template <typename T>
int addGet (T & t, std::true_type const &)
{ return t.get(); }
template <typename T>
int addGet (T & t, std::false_type const &)
{ return 0; }
template <typename T>
int addGet (T & t)
{ return addGet(t, withGet<T>{}); }
--EDIT--
The OP ask
my code needs to check for a templated method with parameters. Is it possible to modify your solution so that instead of int get() it could check for something like template<class T, class U> void process(T& t, U& u)?
I suppose it's possible.
You can create a type-traits withProcess2 (where 2 is the number of arguments) that receive three template type parameters: the class and the two template types
template <typename, typename, typename>
constexpr std::false_type withProcess2H (long);
template <typename T, typename U, typename V>
constexpr auto withProcess2H (int)
-> decltype( std::declval<T>().process(std::declval<U>(),
std::declval<V>()),
std::true_type{} );
template <typename T, typename U, typename V>
using withProcess2 = decltype( withProcess2H<T, U, V>(0) );
The following is a fully working example (c++17, but now you know how to make it c++14) with A and C with a process() with 2 template parameters and B with a process() with only 1 template parameter.
#include <iostream>
#include <type_traits>
template <typename, typename, typename>
constexpr std::false_type withProcess2H (long);
template <typename T, typename U, typename V>
constexpr auto withProcess2H (int)
-> decltype( std::declval<T>().process(std::declval<U>(),
std::declval<V>()),
std::true_type{} );
template <typename T, typename U, typename V>
using withProcess2 = decltype( withProcess2H<T, U, V>(0) );
struct A
{
template <typename T, typename U>
void process(T const &, U const &)
{ std::cout << "A::process(T, U)" << std::endl; }
};
struct B
{
template <typename T>
void process(T const &)
{ std::cout << "B::process(T)" << std::endl; }
};
struct C
{
template <typename T, typename U>
void process(T &, U &)
{ std::cout << "C::process(T, U)" << std::endl; }
};
template <typename T>
void callProcess (T & t)
{
static int i0 { 0 };
static long l0 { 0L };
if constexpr ( withProcess2<T, int &, long &>{} )
t.process(i0, l0);
}
int main ()
{
std::tuple<A, B, C> t;
callProcess(std::get<0>(t)); // print A::process(T, U)
callProcess(std::get<1>(t)); // no print at all
callProcess(std::get<2>(t)); // print C::process(T, U)
}
I am trying to achieve the following:
template<template<typename> bool Function_, typename ... Types_>
constexpr auto find(Tuple<Types_ ... >) noexcept
{
// ...
}
where a possible function could be:
template<typename T>
inline constexpr bool is_pointer_v = is_pointer<T>::value;
so then the usage of find would be:
Tuple<int, char, void *> t;
find<is_pointer_v>(t);
don't worry about the implementation of find, I am just asking about how to do "template < typename > bool Function_" as the bool part is invalid in c++ currently.
any help is appreciated!
EDIT:
here is an example of why I can't pass the "is_pointer" to the function:
template<typename T_>
constexpr auto add_pointer(Type<T_>) noexcept
{ return type_c<T_ *>; }
template<typename F_, typename T_>
constexpr auto apply(F_ f, Type<T_> t) noexcept
{
return f(t);
}
int main(void)
{
Type<int> t_i;
apply(add_pointer, t_i);
}
this produces the compiler error:
error: no matching function for call to ‘apply(< unresolved overloaded function type >, sigma::meta::Type&)’
apply(add_pointer, t_i);
any help is appreciated!
You can simply wrap your functions within functors.
As a minimal, working example:
template<typename>
struct Type {};
template<typename>
struct type_c {};
template<typename T_>
struct add_pointer {
static constexpr auto invoke(Type<T_>) noexcept
{ return type_c<T_ *>{}; }
};
template<template<typename> class F_, typename T_>
constexpr auto apply(Type<T_> t) noexcept {
return F_<T_>::invoke(t);
}
int main(void) {
Type<int> t_i;
apply<add_pointer>(t_i);
}
If you can't change them directly, create functors that forward everything to the right function through a static constexpr member method.
I am just asking about how to do "template < typename > bool Function_" as the bool part is invalid in c++ currently.
As far I know, template-template arguments are a completely different thing. They are intended for containers, not for functions. So class, not bool.
here is an example of why I can't pass the "is_pointer" to the function
Your example doesn't work because add_pointer is a template function, so when you call
apply(add_pointer, t_i);
the compiler doesn't know which version (which type T) of add_pointer to use.
A solution can be explicit it, as in the following simplified example
#include <tuple>
#include <iostream>
template <typename T>
constexpr auto add_pointer(std::tuple<T>) noexcept
{ std::cout << "add_pointer" << std::endl; return 0; }
template <typename F, typename T>
constexpr auto apply(F f, std::tuple<T> t) noexcept
{ return f(t); }
int main(void)
{
std::tuple<int> t_i { 1 };
apply<int(*)(std::tuple<int>)>(add_pointer, t_i);
}
but I understand that explicating int(*)(std::tuple<int>) is a big pain in the ass.
You can simplify a little using the fact that you pass t so you can deduce the type of the argument received by the function, but (for a generic solution) I don't know how to avoid to explicit the return type of the function (maybe it's possible, but (in this moment) I don't know.
So you can simplify the call as follows
apply<int>(add_pointer, t_i);
and the following is a little more general example
#include <tuple>
#include <iostream>
template <typename ... Ts>
constexpr auto add_pointer(std::tuple<Ts...> const &) noexcept
{ std::cout << "add_pointer" << std::endl; return 0; }
template <typename R, typename ... Ts,
typename F = R(*)(std::tuple<Ts...> const &)>
constexpr auto apply(F f, std::tuple<Ts...> t) noexcept
{ return f(t); }
int main(void)
{
std::tuple<int> t_i { 1 };
apply<int>(add_pointer, t_i);
}
I'm writing a contains() utility function and have come up with this. My question is: Is there a nicer way to select the right function to handle the call?
template <class Container>
inline auto contains(Container const& c,
typename Container::key_type const& key, int) noexcept(
noexcept(c.end(), c.find(key))) ->
decltype(c.find(key), true)
{
return c.end() != c.find(key);
}
template <class Container>
inline auto contains(Container const& c,
typename Container::value_type const& key, long) noexcept(
noexcept(c.end(), ::std::find(c.begin(), c.end(), key))
)
{
auto const cend(c.cend());
return cend != ::std::find(c.cbegin(), cend, key);
}
template <class Container, typename T>
inline auto contains(Container const& c, T const& key) noexcept(
noexcept(contains(c, key, 0))
)
{
return contains(c, key, 0);
}
For the record, you could write:
#include "magic.h"
template <typename T, typename... Us>
using has_find = decltype(std::declval<T>().find(std::declval<Us>()...));
template <class Container, typename T>
auto contains(const Container& c, const T& key)
{
return static_if<detect<has_find, decltype(c), decltype(key)>{}>
(
[&] (auto& cont) { return cont.end() != cont.find(key); },
[&] (auto& cont) { return cont.end() != std::find(cont.begin(), cont.end(), key); }
)(c);
}
where magic.h contains:
#include <type_traits>
template <bool> struct tag {};
template <typename T, typename F>
auto static_if(tag<true>, T t, F f) { return t; }
template <typename T, typename F>
auto static_if(tag<false>, T t, F f) { return f; }
template <bool B, typename T, typename F>
auto static_if(T t, F f) { return static_if(tag<B>{}, t, f); }
template <bool B, typename T>
auto static_if(T t) { return static_if(tag<B>{}, t, [](auto&&...){}); }
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, Operation, Args...>;
DEMO
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=typename details::can_apply<Z,void,Ts...>::type;
this takes a template and arguments, and tells you if you can apply it.
template<class T, class...Args>
using dot_find_r = decltype(std::declval<T>().find(std::declval<Args>()...));
template<class T, class...Args>
constexpr can_apply<dot_find_r, T, Args...> can_dot_find{};
we now tag dispatch on myfind:
template<class C>
using iterator = decltype( ::std::begin(std::declval<C>()) );
namespace details {
template<class Container, class Key>
iterator<Container const&> myfind(
std::false_type can_dot_find,
Container const& c,
Key const& key
)
noexcept(
noexcept( ::std::find(::std::begin(c), ::std::end(c), key) )
)
{
return ::std::find( ::std::begin(c), ::std::end(c), key );
}
template <class Container, class Key>
iterator<Container const&> myfind(
std::true_type can_dot_find,
Container const& c,
Key const& key
) noexcept(
noexcept( c.find(key) )
)
{
return c.find(key);
}
}
template<class Container, class Key>
iterator<Container const&> myfind(
Container const& c,
Key const& k
) noexcept (
details::myfind( can_dot_find<Container const&, Key const&>, c, k )
)
{
return details::myfind( can_dot_find<Container const&, Key const&>, c, k );
}
template<class Container, class Key>
bool contains(
Container const& c,
Key const& k
) noexcept (
noexcept( ::std::end(c), myfind( c, k ) )
)
{
return myfind(c, k) != ::std::end(c);
}
As a bonus, the above version works with raw C style arrays.
The next enhancement I'd do would be an auto-ADL std::begin to make begin extensions work in the non-dot_find case.
My personal equivalent returns a std::optional<iterator> of the appropriate type. This both provides a quick "is it there", and gives easy access to the iterator if not not there.
if (auto oit = search_for( container, key )) {
// use *oit here as the iterator to the element, guaranteed not to be `end`
}
or
if (search_for( container, key )) {
// key was there
}
but that is neither here nor there.
So you want to call c.find if possible else std::find. But also being wary of type ambiguity as in std::set.
Here is the code to solve that (with the verbose and micro-optimization removed in favor of readability):
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <type_traits>
#include <set>
#include <cstdarg>
using namespace std;
template <typename T, typename Ret>
struct dummy {
typedef Ret type;
};
template <class Container>
auto contains(const Container &c, typename Container::key_type const &key) ->
typename dummy<decltype(c.find(key)), bool>::type {
cout << "c.find" << endl;
return c.end() != c.find(key);
}
template <class Container, typename ...T>
typename std::enable_if<sizeof...(T)==1, bool>::type contains(const Container &c, const T&... args) {
typename Container::value_type const &val = std::get<0>(std::tuple<const T&...>(args...));
cout << "std::find" << endl;
return c.cend() != find(c.cbegin(), c.cend(), val);
}
int main() {
vector<int> v = {1,2,3};
cout << contains(v,4) << contains(v,2) << endl;
map<int, int> m;
m[1] = 1;
m[2] = 2;
m[3] = 3;
cout << contains(m,4) << contains(m,2) << endl;
set<int> s;
cout << contains(s,4) << contains(s,2) << endl;
return 0;
}
What I did:
I made the first contains function dependent on c.find() being callable. When it's not, the compiler doesn't see the function, and no issues arise
I resolved ambiguity when key_type and value_type are the same, by introducing the function using std::find with its second mandatory argument as a variadic template. I also forced the variadic template of being of size 1.
If you just assume that key_type existing means that container.find exists as in OP, then you can simplify the code and remove the dummy structure:
template <class Container>
bool contains(const Container &c, typename Container::key_type const &key)
{
return c.end() != c.find(key);
}
template <class Container, typename ...T>
typename std::enable_if<sizeof...(T)==1, bool>::type contains(const Container &c, const T&... args) {
typename Container::value_type const &val = std::get<0>(std::tuple<const T&...>(args...));
return c.cend() != find(c.cbegin(), c.cend(), val);
}
Instead of having to resolve ambiguity that way, it's possible to disable the second function altogether if Container::find does exist. This and That answer both show different ways of knowing so. Then using std::enable_if<! (Does Container have the find method?) , bool>::type as the return type of the second function will work.
since find is exist in associative containers then you can explicitly make them as true types.
meta-functions:
template <class> struct has_find_impl:std::false_type{};
template <class T, class... Args> struct has_find_impl<std::set<T, Args...>>:std::true_type{};
template <class T, class... Args> struct has_find_impl<std::map<T, Args...>>:std::true_type{};
template <class T, class... Args> struct has_find_impl<std::multiset<T, Args...>>:std::true_type{};
template <class T, class... Args> struct has_find_impl<std::multimap<T, Args...>>:std::true_type{};
template <class T> using has_find = has_find_impl<typename std::decay<T>::type>;
and use it like so:
template <class Container>
bool contains_impl(const Container& c, const typename Container::key_type& key, std::true_type)
{
return c.find(key) != c.cend();
}
template <class Container>
bool contains_impl(const Container& c, typename Container::const_reference key, std::false_type)
{
return std::find(c.cbegin(), c.cend(), key) != c.cend();
}
template <class Container, class T>
bool contains(const Container& c, const T& key)
{
return contains_impl(c, key, has_find<Container>{});
}
or used it with SFINAE. here a complete example:
#include <iostream>
#include <algorithm>
#include <utility>
#include <map>
#include <set>
#include <vector>
#include <array>
#include <type_traits>
template <class> struct has_find_impl:std::false_type{};
template <class T, class... Args> struct has_find_impl<std::set<T, Args...>>:std::true_type{};
template <class T, class... Args> struct has_find_impl<std::map<T, Args...>>:std::true_type{};
template <class T, class... Args> struct has_find_impl<std::multiset<T, Args...>>:std::true_type{};
template <class T, class... Args> struct has_find_impl<std::multimap<T, Args...>>:std::true_type{};
template <class T> using has_find = has_find_impl<typename std::decay<T>::type>;
template <class Container>
typename std::enable_if<has_find<Container>::value, bool>::type
contains_impl(const Container& c, const typename Container::key_type& key)
{
return c.find(key) != c.cend();
}
template <class Container>
typename std::enable_if<!has_find<Container>::value, bool>::type
contains_impl(const Container& c, typename Container::const_reference key)
{
return std::find(c.cbegin(), c.cend(), key) != c.cend();
}
template <class Container, class T>
bool contains(const Container& c, const T& key)
{
return contains_impl(c, key);
}
int main()
{
std::cout << std::boolalpha;
std::array<int, 3> a = {{ 1, 2, 3 }};
std::cout << contains(a, 0) << "\n";
std::cout << contains(a, 1) << "\n\n";
std::vector<int> v = { 1, 2, 3 };
std::cout << contains(v, 0) << "\n";
std::cout << contains(v, 1) << "\n\n";
std::set<int> s = { 1, 2, 3 };
std::cout << contains(s, 0) << "\n";
std::cout << contains(s, 1) << "\n\n";
std::map<int, int> m = { { 1, 1}, { 2, 2}, { 3, 3} };
std::cout << contains(m, 0) << "\n";
std::cout << contains(m, 1) << "\n\n";
}
I have the following code:
#include <array>
#include <iostream>
#include <typeinfo>
#include <type_traits>
#include <utility>
namespace impl
{
template <typename T>
struct Matrix_traits {
};
}
template <size_t M, size_t N, typename T>
class Matrix
{
};
template <size_t M, size_t N, typename T>
struct impl::Matrix_traits<Matrix<M, N, T>> {
template <typename U>
struct scalar_mult_type
{
// just for testing
using type = std::pair<std::array<T, M>, std::array<T, N>>;
};
};
int main()
{
Matrix<3, 4, char> m;
mult(3, m);
return 0;
}
When I use the following function implementation, where I'm explicitly specifying the return type:
template <typename T, typename U>
std::pair<std::array<char, 3>, std::array<char, 4>> mult(const T& lambda, const U& m)
{
typename impl::Matrix_traits<U>::scalar_mult_type<T>::type result;
std::cout << typeid(result).name() << "\tEUREKA!\n";
return result;
}
It works... but this is obviously not what I want... but when I'm trying to be more flexible:
template <typename T, typename U>
typename impl::Matrix_traits<U>::scalar_mult_type<T>::type mult(const T& lambda, const U& m)
{
typename impl::Matrix_traits<U>::scalar_mult_type<T>::type result;
std::cout << typeid(result).name() << "\tEUREKA!\n";
return result;
}
I have "unrecognizable template declaration/definition" error.
It's a real puzzle to me. Why the same declaration works for local variable 'result' but fails as the return type?
template are missing, it should be
template <typename T, typename U>
typename impl::Matrix_traits<U>::template scalar_mult_type<T>::type
mult(const T& , const U& )
{
typename impl::Matrix_traits<U>::template scalar_mult_type<T>::type result;
std::cout << typeid(result).name() << "\tEUREKA!\n";
return result;
}
Demo