I'm writing a template function that should only accept random access iterator of any container containing a specific type (defined by template ).
At the moment, I'm first trying to limit the type of the iterator using SFINAE but the code does not compile.
#include <iostream>
#include <type_traits>
#include <vector>
template<typename It,
std::enable_if<
std::is_same<typename std::iterator_traits<It>::iterator_category,
std::random_access_iterator_tag>::value,
typename std::iterator_traits<It>::difference_type>>
void func(const It& begin, const It& end)
{
std::cout << begin[0] << std::endl;
}
int main()
{
std::vector<int> a = {0,1,2,3,4,5};
func(a.begin(), a.end());
return 0;
}
The error is:
error: ‘struct std::enable_if<std::is_same<typename
std::iterator_traits<_Iter>::iterator_category,std::random_access_iterator_tag::value,
typename std::iterator_traits<_Iterator>::difference_type>’ is not a
valid type for a template non-type parameter template<typename It,
std::enable_if<std::is_same<typename std::iterator_traits
error: no matching function for call to
‘func(std::vector<int>::iterator, std::vector<int>::iterator)’
func(a.begin(), a.end());
I can't parse your enable_if.
That works:
template<typename It, typename std::enable_if<std::is_same<typename std::iterator_traits<It>::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0>
void func(const It& begin, const It& end)
{
std::cout << begin[0] << std::endl;
}
int main()
{
std::vector<int> a = {0,1,2,3,4,5};
func(a.begin(), a.end());
return 0;
}
But it can be that I misunderstood your intention.
You need typename = to use an anonymous type for SFINAE like that:
template<typename It,
typename = std::enable_if<std::is_same<typename std::iterator_traits<It>::iterator_category,
std::random_access_iterator_tag>::value,
typename std::iterator_traits<It>::difference_type>>
void func(const It& begin, const It& end)
{
std::cout << begin[0] << std::endl;
}
Or, you could use std::enable_if<...>::type as a return type for your function.
If you're working in C++17 or later, consider looking into the Concepts proposal that's making its way into C++20.
Related
I need to learn how to use enable_if. For this I need to reimplement the distance function using enable_if. I tried this:
#include <iostream>
#include <vector>
#include <list>
#include <utility>
#include <type_traits>
template<class In>
typename std::enable_if<!std::is_random_acces_iterator<In>::value, std::iterator_traits<In>::difference_type>::type my_distance(In begin, In end, std::input_iterator_tag dummy){
typename std::iterator_traits<In>::difference_type n = 0;
while(begin!=end){
++begin; ++n;
}
std::cout << "STEPPING" << std::endl;
return n;
}
template<class Ran>
typename std::enable_if<std::is_random_acces_iterator<Ran>::value, std::iterator_traits<In>::difference_type>::type my_distance(Ran begin, Ran end, std::random_access_iterator_tag dummy){
std::cout << "RANDOM" << std::endl;
return end - begin;
}
template <class I> inline
typename std::iterator_traits<I>::difference_type my_distance_wrapper(I begin, I end){
typedef typename std::iterator_traits<I>::iterator_category cat;
return my_distance(begin, end, cat());
}
int main() {
std::vector<int> ve;
std::list<int> li;
for(int i = 0; i < 3; i++){
ve.push_back(i);
li.push_back(i);
}
std::cout << my_distance_wrapper(ve.begin(), ve.end()) << std::endl;
std::cout << my_distance_wrapper(li.begin(), li.end()) << std::endl;
return 0;
}
I thought I could do this by using some function like std::is_random_acces_iterator<In>::value similar to std::is_pod<In>::value
I checked the type_traits but could not find anything for checking if something is a specific iterator.
How would I check if something is an random_acces_iterator? I know I could just do this in a function:
template<class T>
bool f(){
typedef typename std::iterator_traits<T>::iterator_category cat;
return std::random_access_iterator_tag == cat();
}
So my question is basically how do I do function f in a template? And no not using enable_if is not possible, it is a requirement of my task. And am I correct to believe that SFINAE would correctly discard the other function, if I could get function f into that template?
You can use the type traits std::is_same<> as follows
template<typename iterator>
using is_random_access_iterator =
std::is_same<typename std::iterator_traits<iterator>::iterator_category,
std::random_access_iterator_tag>;
and then
template<typename iterator>
std::enable_if_t<is_random_access_iterator<iterator>::value,
typename std::iterator_traits<iterator>::difference_type>
my_distance(iterator a, iterator b) { return a-b; }
template<typename iterator>
std::enable_if_t<!is_random_access_iterator<iterator>::value,
typename std::iterator_traits<iterator>::difference_type>
my_distance(iterator a, iterator b) { /* ... */ }
Here, std::enable_if_t<,> is a helper alias defined (since C++14) as
template<bool condition, typename type = void>
using enable_if_t = typename enable_if<condition,type>::type;
Actually, you can also declare your function f() as constexpr and use it directly within the enable_if, i.e. std::enable_if<f<It>(), ...>.
Suppose we have this template
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
...
}
How can it be stated that, obviously the elements in container should be of type T?
Can this all be abbreviated (maybe in C++11)?
While other answers using value_type are correct , the canonical solution to this frequent problem is to not pass the container in the first place : use the Standard Library semantics, and pass a pair of iterators.
By passing iterators, you don't have to worry about the container itself. Your code is also much more generic : you can act on ranges, you can use reversed iterators, you can combine your template with other standard algorithms etc.. :
template<typename Iterator, typename T>
bool contains (Iterator begin, Iterator end, const T& value) {
...
}
int main(){
std::vector<int> v { 41, 42 };
contains(std::begin(v), std::end(v), 42);
};
If you want to check the type carried by Iterator, you can use std::iterator_traits :
static_assert(std::is_same<typename std::iterator_traits<Iterator>::value_type, T>::value, "Wrong Type");
(Note that this assertion is generally not needed : if you provide a value not comparable with T, the template will not compile in the first place)
The final template would look like :
template<typename Iterator, typename T>
bool contains (Iterator begin, Iterator end, const T& value) {
static_assert(std::is_same<typename std::iterator_traits<Iterator>::value_type, T>::value, "Wrong Type");
while(begin != end)
if(*begin++ == value)
return true;
return false;
}
Live demo
Notes:
1) This should not be a surprise, but our contains template now has almost the same signature than std::find (which returns an iterator) :
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
2) If modifying the signature of the original contains is too much, you can always forward the call to our new template :
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
return contains(std::begin(theContainer), std::end(theContainer), theReference);
}
You might restrict the container type in the template:
#include <algorithm>
#include <iostream>
#include <vector>
template< template<typename ... > class Container, typename T>
bool contains(const Container<T>& container, const T& value) {
return std::find(container.begin(), container.end(), value) != container.end();
}
int main()
{
std::vector<int> v = { 1, 2, 3 };
std::cout << std::boolalpha
<< contains(v, 0) << '\n'
<< contains(v, 1) << '\n';
// error: no matching function for call to ‘contains(std::vector<int>&, char)’
contains(v, '0') ;
return 0;
}
A more complete solution (addressing some comments):
#include <algorithm>
#include <array>
#include <iostream>
#include <map>
#include <set>
#include <vector>
// has_member
// ==========
namespace Detail {
template <typename Test>
struct has_member
{
template<typename Class>
static typename Test::template result<Class>
test(int);
template<typename Class>
static std::false_type
test(...);
};
}
template <typename Test, typename Class>
using has_member = decltype(Detail::has_member<Test>::template test<Class>(0));
// has_find
// ========
namespace Detail
{
template <typename ...Args>
struct has_find
{
template<
typename Class,
typename R = decltype(std::declval<Class>().find(std::declval<Args>()... ))>
struct result
: std::true_type
{
typedef R type;
};
};
}
template <typename Class, typename ...Args>
using has_find = has_member<Detail::has_find<Args...>, Class>;
// contains
// ========
namespace Detail
{
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(std::false_type, const Container<Key, Args...>& container, const Key& value) {
bool result = std::find(container.begin(), container.end(), value) != container.end();
std::cout << "Algorithm: " << result << '\n';;
return result;
}
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(std::true_type, const Container<Key, Args...>& container, const Key& value) {
bool result = container.find(value) != container.end();
std::cout << " Member: " << result << '\n';
return result;
}
}
template<template<typename ...> class Container, typename Key, typename ... Args>
bool contains(const Container<Key, Args...>& container, const Key& value) {
return Detail::contains(has_find<Container<Key, Args...>, Key>(), container, value);
}
template<typename T, std::size_t N>
bool contains(const std::array<T, N>& array, const T& value) {
bool result = std::find(array.begin(), array.end(), value) != array.end();
std::cout << " Array: " << result << '\n';;
return result;
}
// test
// ====
int main()
{
std::cout << std::boolalpha;
std::array<int, 3> a = { 1, 2, 3 };
contains(a, 0);
contains(a, 1);
std::vector<int> v = { 1, 2, 3 };
contains(v, 0);
contains(v, 1);
std::set<int> s = { 1, 2, 3 };
contains(s, 0);
contains(s, 1);
std::map<int, int> m = { { 1, 1}, { 2, 2}, { 3, 3} };
contains(m, 0);
contains(m, 1);
return 0;
}
For standard container, you may use value_type:
template<typename Container>
bool contains (const Container & theContainer, const typename Container::value_type& theReference) {
...
}
Note that there is also const_reference in your case:
template<typename Container>
bool contains (const Container & theContainer, typename Container::const_reference theReference) {
...
}
You can check the value_type of container and T using static_assert
template<typename Container, typename T>
bool contains (const Container & theContainer, const T & theReference) {
static_assert( std::is_same<typename Container::value_type, T>::value,
"Invalid container or type" );
// ...
}
Demo Here
Using std::enable_if (http://en.cppreference.com/w/cpp/types/enable_if), but a little more complicated than with static_assert.
EDIT: According to P0W's comment, using std::enable_if allows us to use SFINAE, which is nice when you decide to have more overloads. For example if the compiler decides to use this templated function, with a Container with no value_type typedefed, it won't generate an error instantly, like static_assert would, just looks for other functions which perfectly fits the signature.
Tested on Visual Studio 12.
#include <vector>
#include <iostream>
template<typename Container, typename T>
typename std::enable_if<
std::is_same<T, typename Container::value_type>::value, bool>::type //returns bool
contains(const Container & theContainer, const T & theReference)
{
return (std::find(theContainer.begin(), theContainer.end(), theReference) != theContainer.end());
};
int main()
{
std::vector<int> vec1 { 1, 3 };
int i = 1;
float f = 1.0f;
std::cout << contains(vec1, i) << "\n";
//std::cout << contains(vec1, f); //error
i = 2;
std::cout << contains(vec1, i) << "\n";
};
output:
1
0
PS: Your original function does it too, except that allows derived classes too. These solutions does not.
I'm looking for a reasonable way to select a sort algorithm based on the value type of the container.
In its current form I can deduce the proper sort(a, b) for integer/non-integer data.
#include <cstdlib>
#include <type_traits>
#include <algorithm>
#include <vector>
#include <iostream>
namespace sort_selector{
template<typename T>
void _radix_sort(T begin, T end){
// radix implementation
}
template<typename T>
typename std::enable_if<
std::is_integral<typename T::value_type>::value>::type
sort(T begin, T end){
std::cout << "Doing radix" << std::endl;
sort_selector::_radix_sort(begin, end);
}
template<typename T>
typename std::enable_if<
!std::is_integral<typename T::value_type>::value>::type
sort(T begin, T end){
std::cout << "Doing sort" << std::endl;
std::sort(begin, end);
}
}
int main(int argc, char** argv) {
std::vector<double> for_stdsort = {1, 4, 6, 2};
std::vector<int32_t> for_radixsort = {1, 4, 6, 2};
//std::array<int32_t, 4> array_for_radixsort = {1, 4, 6, 2};
sort_selector::sort(std::begin(for_stdsort), std::end(for_stdsort));
sort_selector::sort(std::begin(for_radixsort), std::end(for_radixsort));
//sort_selector::sort(std::begin(array_for_radixsort),
// std::end(array_for_radixsort));
return 0;
}
I would like to be able to use array-like iterators. (they have no ::value_type).
I would like to be able to distinguish between say, int32_t and int64_t.
I'm at a complete loss as how to achieve this in any reasonably simple way. I.e. not specializing for every instance.
Use std::iterator_traits<T>::value_type to retrieve the value type of an iterator; it works for pointers as well as class-type iterators.
For dispatching, I would use template specialization to select the proper implementation (Live Demo):
namespace sort_selector {
// Default to using std::sort
template <typename T, typename = void>
struct dispatcher {
template <typename Iterator>
static void sort(Iterator begin, Iterator end) {
std::cout << "Doing std::sort\n";
std::sort(begin, end);
}
};
// Use custom radix sort implementation for integral types
template <typename T>
struct dispatcher<T, typename std::enable_if<std::is_integral<T>::value>::type> {
template <typename Iterator>
static void sort(Iterator, Iterator) {
std::cout << "Doing radix\n";
// radix implementation
}
};
// Use some other specific stuff for int32_t
template <>
struct dispatcher<int32_t, void> {
template <typename Iterator>
static void sort(Iterator, Iterator) {
std::cout << "Specific overload for int32_t\n";
// Do something
}
};
// Dispatch appropriately
template <typename Iterator>
inline void sort(Iterator begin, Iterator end) {
dispatcher<typename std::iterator_traits<Iterator>::value_type>::sort(begin, end);
}
} // namespace sort_selector
You should probably constrain sort_selector::sort to require random access iterators so your error messages are more digestible when someone inevitably tries to pass an improper iterator type:
namespace sort_selector {
// Dispatch appropriately
template <typename Iterator>
inline void sort(Iterator begin, Iterator end) {
using traits = std::iterator_traits<Iterator>;
static_assert(
std::is_base_of<
std::random_access_iterator_tag,
typename traits::iterator_category
>::value, "sorting requires random access iterators");
dispatcher<typename traits::value_type>::sort(begin, end);
}
} // namespace sort_selector
I have encountered some unexpected results while playing around with my personal formatting library. I have reduced the code to the listing you can find below or on coliru.
#include <iostream>
#include <map>
#include <utility>
#include <string>
template <typename T>
struct executioner {
inline static void exec( const T & ){
std::cout << "generic" << std::endl;
}
};
template <typename T1, typename T2>
struct executioner<std::pair<T1, T2> > {
inline static void exec( const std::pair<T1, T2> & t ){
std::cout << "pair" << std::endl;
executioner<T1>::exec( t.first );
executioner<T2>::exec( t.second );
}
};
template <template <typename ...> class Container,
typename ... Ts>
struct executioner<Container<Ts ...> > {
inline static void exec( const Container<Ts ...> & c ){
std::cout << "cont" << std::endl;
auto it = c.begin();
typedef decltype( * it ) ContainedType;
executioner<ContainedType>::exec( * it );
}
};
template <typename T> void execute( const T & t ){
executioner<T>::exec( t );
}
int main(){
std::map<int,std::string> aMap = { { 0, "zero" }, { 1, "one" }, { 2, "two" } };
execute( aMap );
}
Note that the code is reduced for demonstration, in the actual piece, I'd use the iterator in the variadic function to loop over the input container and call executioner<ContainedType>::exec( * it ); for each of the container's elements.
Executing this code, I would expect the following output:
cont
pair
generic
To my surprise, the specialisation for std::pair was not used, the actual output is:
cont
generic
I very much doubt that this is a compiler bug (since it happens with both gcc 4.9 and clang 3.2) and therefore I ask
What's the catch I haven't thought about?
Change your variadic template code to the following:
template <template <typename ...> class Container,
typename ... Ts>
struct executioner<Container<Ts ...> > {
inline static void exec(const Container<Ts ...> & c){
std::cout << "cont" << std::endl;
auto it = c.begin();
executioner<typename Container<Ts...>::value_type>::exec(*it);
}
};
and your code will work as expected.
Reason:
I'll try to demonstrate why decltype doesn't work as expected with an example:
template <template <typename ...> class Container,
typename ... Ts>
struct executioner<Container<Ts ...> > {
inline static void exec( const Container<Ts ...> & c ){
std::cout << "cont" << std::endl;
auto it = c.begin();
typedef decltype( (*it) ) ContainedType;
if(std::is_same<decltype(it->first), const int>::value) {
std::cout << "IS CONST !!!" << std::endl;
}
executioner<ContainedType>::exec( * it );
}
};
If you run substituting with the code above you'll see that conditional std::is_same<decltype(it->first), const int>::value is true. This means that the *it is of type std::pair<const int, std::basic_string<...>> and not std::pair<int, std::basic_string<...>>. Thus, your specialization "fails".
Ok, it's all coming back to me now :)
As ildjarn hinted you can use std::remove_reference to strip the reference. I also stripped the const qualifier (see this thread). My convenience struct looks like this:
template <typename T>
struct to_value
{
typedef typename std::remove_const<
typename std::remove_reference< T >::type
>::type type;
};
You call it like this:
typedef decltype( *it ) ContainedType;
executioner< typename to_value<ContainedType>::type >::exec( *it );
Now you only need to specialize on the value type. Here's the whole thing.
The typedef ContainedType is not std::pair<int, std::string>. It is const std::pair<const int, std::string>&. That is why your first partial specialization does not match. You can modify it that way:
template <typename T1, typename T2>
struct executioner<const std::pair<T1, T2>&> {
inline static void exec( const std::pair<T1, T2> & t ) {
std::cout << "pair" << std::endl;
executioner<T1>::exec( t.first );
executioner<T2>::exec( t.second );
}
};
and it matches (coliru link).
Or you can use the value_type of the container, instead of decltype(*it):
typedef typename Container<Ts...>::value_type ContainedType;
in the later case, ContainedType is std::pair<const int, std::string> as expected (coliru link).
I need a function template that accepts two iterators that could be pointers. If the two arguments are random_access iterators I want the return type to be an object of
std::iterator<random_access_iterator_tag, ...> type
else a
std::iterator<bidirectional_iterator_tag, ...> type.
I also want the code to refuse
compilation if the arguments are neither a bidirectional iterator, nor a pointer. I cannot have dependency on third party libraries e.g. Boost
Could you help me with the signature of this function so that it accepts bidirectional iterators as well as pointers, but not say input_iterator, output_iterator, forward_iterators.
One partial solution I can think of is the following
template<class T>
T foo( T iter1, T iter2) {
const T tmp1 = reverse_iterator<T>(iter1);
const T tmp2 = reverse_iterator<T>(iter2);
// do something
}
The idea is that if it is not bidirectional the compiler will not let me construct a reverse_iterator from it.
Here's an example with enable_if based on iterator tags. The substitution fails if the given T doesn't have a iterator_category typedef and so that overload isn't considered during overload resolution.
Since you can't use C++11, see the reference pages for enable_if and is_same to see how you can implement it by yourself.
#include <iterator>
#include <type_traits>
#include <iostream>
#include <vector>
#include <list>
template<typename T>
typename
std::enable_if<
std::is_same<
typename T::iterator_category,
std::bidirectional_iterator_tag
>::value,
T
>::type
foo(T it)
{
std::cout << "bidirectional\n";
return it;
}
template<typename T>
typename
std::enable_if<
std::is_same<
typename T::iterator_category,
std::random_access_iterator_tag
>::value,
T
>::type
foo(T it)
{
std::cout << "random access\n";
return it;
}
// specialization for pointers
template<typename T>
T* foo(T* it)
{
std::cout << "pointer\n";
return it;
}
int main()
{
std::list<int>::iterator it1;
std::vector<int>::iterator it2;
int* it3;
std::istream_iterator<int> it4;
foo(it1);
foo(it2);
foo(it3);
//foo(it4); // this one doesn't compile, it4 is an input iterator
}
Live example.
As per #JonathanWakely's comment, we can get rid of specialization for pointers if we use std::iterator_traits. The typename T::iterator_category part then becomes
typename std::iterator_traits<T>::iterator_category
a bit simpler than previous answer, no dependency on std::enable_if:
namespace detail
{
template<class T>
T do_foo(T iter1, T iter2, std::random_access_iterator_tag t)
{
cout << "do_foo random_access" << endl;
return iter1;
}
template<class T>
T do_foo(T iter1, T iter2, std::bidirectional_iterator_tag t)
{
cout << "do_foo bidirectional" << endl;
return iter1;
}
}
template<class T>
void foo(T iter1, T iter2)
{
typename std::iterator_traits<T>::iterator_category t;
detail::do_foo(iter1, iter2, t);
}
int main (int argc, const char * argv[])
{
std::vector<int> v;
foo(v.begin(), v.end());
std::list<int> l;
foo(l.begin(), l.end());
return 0;
}
The solution also supports other iterator_categories derived from std::random_access_iterator_tag or std::bidirectional_iterator_tag (should there be any), while std::same<> checks for strict category equality.