I am playing around with a generic template template function to output containers to the console.
To do this, I wish to provide a defaulted separator. It works defaulting a template argument to const char (but then I need to do a silly string construction) or defaulting the const char * function argument (I know this is a perfectly good solution and might be the preferred).
What I want to find out is if there is a way to default the template argument to a specific const char* value? Because the template arguments are meant to declare types not derivatives like (*, &, etc. - yet const works?) I am not sure it is possible but a defaulted template argument and more efficient than the first implementation underneath is what I am looking for.
#include <cstdlib>
#include <iostream>
#include <thread>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;
// This Works
template <template <typename , typename > class Container, class Element, class Allocator, const char sep= ','>
void displayContents(const Container<Element, Allocator>& inputContainer)
{
string seperator(1, sep);
copy(inputContainer.cbegin(), inputContainer.cend(), ostream_iterator<Element>(cout, seperator.c_str())); // not all that efficient...
cout << endl;
};
// This works fine
template <template <typename, typename > class Container, class Element, class Allocator>
void displayContentsWithDefaultArgs(const Container<Element, Allocator>& inputContainer, const char* sep = ",")
{
copy(inputContainer.cbegin(), inputContainer.cend(), ostream_iterator<Element>(cout, sep));
cout << endl;
};
// how can we get this to work (more effectively than displayContents above?)
template <template <typename, typename > class Container, class Element, class Allocator, const char* sep = ",">
void displayContentsWithDefaultTemplateArgs(const Container<Element, Allocator>& inputContainer)
{
copy(inputContainer.cbegin(), inputContainer.cend(), ostream_iterator<Element>(cout, sep));
cout << endl;
};
int main(int argc, char** argv) {
vector<int> v = { 1 ,2 ,3, 4 };
vector<int> v1;
copy(v.cbegin(), v.cend(), back_inserter(v1));
displayContents(v1);
displayContentsWithDefaultArgs(v1);
displayContentsWithDefaultTemplateArgs(v1);
return 0;
}
It seems to me you want to combine two separate things in one: default type and default variable value of that type. But that's impossible and it seems pretty reasonable to me.
Slightly flexible approach will do what you asked for:
template <template <typename, typename > class Container, class Element, class Allocator, class Sep = const char*>
void displayContentsWithDefaultTemplateArgs(const Container<Element, Allocator>& inputContainer, Sep sep = ",")
{
copy(inputContainer.cbegin(), inputContainer.cend(), ostream_iterator<Element>(cout, sep));
cout << endl;
};
Related
I'd like to manipulate some data with certain types in a template argument list.
In my case, I would like to check if some of the elements are some kind of an Iterable, and if there are any, then I want to use std::advance on them.
This is what I had in mind: (well it obviously will not compile but gives you the right idea what I want to achieve here)
#include <typeinfo>
#include <thread>
template<class _Func, class Iterator, class... _Args>
void
start(_Func pFunc, const Iterator begin, const Iterator end, _Args&&... args)
{
Iterator pt = begin;
Iterator ptNext = begin;
for (unsigned int i = 0; i < this->nThreads(); i++)
{
std::advance(ptNext, partialSize);
this->getThreads(i) = std::thread(pFunc, pt, ptNext, std::forward<_Args>(args)...);
pt = ptNext;
[](...){}((typeid(args) == typeid(Iterator) ? std::advance(args, partialSize) : false)...);
}
}
I think the problem's (maybe?) that, that the argument list gets expanded at compile time and then it will see, that I want to use std::advance on something, that may not even be an Iterable type.
In the code above begin and end are iterators of a data sequence and the partialSize variable tells, that a thread should process only a part of the sequence.
So the goal is, if any other Iterable types are passed through the argument list, say: std::vector<>::iterator or std::list<>::iterator or even a double*, then I would like to std::advance them.
Are there any solutions for this problem? Can I achieve something like this?
I've implemented a function that advances all iterators passed to it. It simply ignores arguments of any other type.
The code requires C++17, but can be ported to earlier versions of standard.
#include <iterator>
#include <type_traits>
template <class T, class = void>
struct is_iterator : std::false_type {};
template <class T>
struct is_iterator<T, std::void_t<typename std::iterator_traits<T>::iterator_category>> : std::true_type {};
template <class Distance, class T>
void advance_if_iterable_impl(Distance n, T& t)
{
if constexpr (is_iterator<T>::value)
std::advance(t, n);
}
template <class Distance, class... Args>
void advance_if_iterable(Distance n, Args&... args)
{
(advance_if_iterable_impl(n, args), ...);
}
#include <iostream>
int main()
{
int i = 42;
const char* str = "Hello World!\n";
advance_if_iterable(2, i, str);
// `int` isn't an iterator, it stays the same
std::cout << i << '\n';
// `const char*` is a random iterator, so it was advanced by two
std::cout << str;
return 0;
}
Cant understand what is wrogn with code, second function definition or call of this function in main?
I think, but not sure, problem in call, cause without calling code compiles well. Compiler gcc
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<class T>
void show_element(T ob)
{
cout << ob << " ";
}
template<template<class> class S, class T>
void show_sequence(S<T> sequence)
{
for_each(sequence.begin(), sequence.end(), show_element<T>);
}
int main(int argc, char const *argv[])
{
std::vector<int> v(20, 0);
//here the problem
show_sequence<std::vector<int>, int>(v);
return 0;
}
std::vector isn't a template of one parameter, it takes an allocator type as well. You can use it as vector<T> simply because the second parameter has a default (std::allocator<T>).
As it's written, your template function cannot accept any standard container, since off the top of my head, none take just a single type parameter.
An approach that would work, and not require you to know how many template parameters a container requires, is to accept a container type (not template), and glean the value type from the container type.
template<class Seq>
void show_sequence(Seq const& sequence)
{
typedef typename Seq::value_type T;
for_each(sequence.begin(), sequence.end(), show_element<T>);
}
All standard containers have a value_type member, so this will work with any of them. Furthermore, it will work with any container that takes its cue from the standard library.
The problem is that std::vector is a template but std::vector<int> is a type.
When you are giving the second one to the function, you are giving one type and not a template.
So, you can rewrite your function as :
template<class S>
void show_sequence(S sequence)
Moreover, vector does not take only one template paramete but two (see StoryTeller answer)
It is similar to this question: https://stackoverflow.com/a/29493191/1889040
It is because vector is template of <type, allocator>
The code should be
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<class T>
void show_element(T ob)
{
cout << ob << " ";
}
template<template<class,class> class S, class T, class Allocator>
void show_sequence(S<T, Allocator> sequence)
{
for_each(sequence.begin(), sequence.end(), show_element<T>);
}
int main(int argc, char const *argv[])
{
std::vector<int> v(20, 0);
//here problem solved
show_sequence<vector, int, allocator<int> > (v);
show_sequence(v);
return 0;
}
I'm trying to make a method that picks a random element from a container type, like std::vector. Before, I was using this:
std::string pick_random(std::vector<std::string> c) {
int r = std::rand() % ids.size() + 1;
auto it = c.begin();
std::advance(it, r);
return *it;
}
which, as far as I could tell, worked fine. That's not to say it is fine, just that it appeared to be.
I soon had to do the same thing for another container, so I tried using template template arguments to make the method generic:
template <template<typename element_t> container_t>
element_t pick_random(container_t from) { /* ... */ }
This, however, throws an error:
element_t does not name a type
I think my intent is clear enough, but to restate it: I'm trying to get the element type of the list. I could have a separate template parameter, but then it can't properly infer the type. I've tried various different versions, but none work.
container_t is not a type, container_t<T> is.
You may use the following:
template <template<typename, typename...> C, typename T, typename...Ts>
T pick_random(const C<T, Ts...>& from);
as for std::vector, you have allocator: std::vector<T, Alloc>.
In C++14, you may simply use auto
template <typename C>
auto pick_random(const C& from) { /* ... */ }
I don't think that "template template arguments" are required here,
you could simply use the value_type from the container:
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <list>
template <typename T>
typename T::value_type pick_random(T& from) {
int r = std::rand() % from.size();
auto it = from.begin();
std::advance(it, r);
return *it;
}
int main() {
std::srand(std::time(0));
std::vector<std::string> words {"the", "frogurt", "is", "also", "cursed"};
std::list<int> numbers {1, 2, 3, 4, 5};
std::cout << "words: " << pick_random(words) << std::endl;
std::cout << "numbers: " << pick_random(numbers) << std::endl;
}
value_type - the type of the values that can be obtained by dereferencing the iterator.
Source: http://en.cppreference.com/w/cpp/iterator/iterator_traits
Even better would be to avoid the arbitrary restriction on class templates. After all, why not be able to pick an element from a raw array? In order to correctly name the type in C++11, we'd have to get the result of an unqualified call to begin, which we can get via:
namespace detail {
using std::begin;
template <typename C>
auto adl_begin(C&& c) -> decltype(begin(std::forward<C>(c))) {
return begin(std::forward<C>(c));
}
}
using detail::adl_begin;
And then use that to deduce the element_type from an arbitrary container:
template <typename C>
auto pick_random(C& container) -> decltype(*adl_begin(container))
{ /* rest as before */ }
Side-note: take your container by reference, not by value.
If you're only using standard library containers then you can get the stored type out of them by using container_t::value_type.
template <typename container_t>
typename container_t::value_type pick_random(container_t& container)
{ ... }
I'm trying to pass an iterator as a template parameter to a template method, but the compiler complains that:
error C2783: 'void Test::Assert(std::vector<T>::const_iterator)':
could not deduce template argument for 'T'
The code that produces the error is:
#include "stdafx.h"
#include <iostream>
#include <vector>
class Test
{
public:
template <typename T>
void Assert(typename std::vector<T>::const_iterator it)
{
std::cout << *it << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Test test;
std::vector<double> myVec;
test.Assert(myVec.cbegin());
return 0;
}
I'm guessing there is a simple way to make this work, since most of the std algorithms can deduce type from iterator.
The reason is that the form you have T in is a non-deduced context:
template <typename T>
void Assert(typename std::vector<T>::const_iterator it)
Consider a simpler case to understand why:
struct A { using type = int; };
struct B { using type = int; };
struct C { using type = int; };
template <typename T>
void Assert(typename T::type it) { ... }
Assert(5);
What should T deduce as? It's impossible to determine. You'd have to explicitly provide the type... as something like Assert<A>(5).
See also What is a nondeduced context?
since most of the std algorithms can deduce type from iterator.
That's because the standard algorithms just deduce the iterator type, not the container type. For instance std::find is just:
template <class InputIt, class T>
InputIt find( InputIt first, InputIt last, const T& value );
There is no concept of "container" here at all - it's just the iterator type that needs to be deduced. That's part of the beauty of the algorithms library.
So if what you want to do is just output the contents of the iterator, the correct function would just be:
template <typename Iterator>
void Assert(Iterator it)
{
std::cout << *it << std::endl;
}
When you call Assert(myVec.cbegin()), Iterator will get deduced as std::vector<double>::const_iterator, which is exactly what you want.
The standard algorithms look like this:
template <typename Iterator>
void some_algorithm(Iterator first, Iterator last) {
// do stuff
}
If they need the type of the iterator, they can use typename std::iterator_traits<Iterator>::value_type.
What they don't do is reference a container such as vector in any way. Not all iterators come from containers.
template <typename Ite>
void Assert(Ite &&it)
{
std::cout << *std::forward<It>(it) << std::endl;
}
That's it - the standard library just parameterizes on the whole type of the iterator. In fact, anything that behaves like an iterator can be used (that's the main reason why iterators behave like pointers). This is called "duck typing".
What you are trying to do (restricting the function to only those types which are explicit iterators) is what C++17 concepts are about.
#include "stdafx.h"
#include <iostream>
#include <vector>
class Test
{
public:
template <typename T>
void Assert(typename T::const_iterator it)
{
std::cout << *it << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Test test;
std::vector<double> myVec;
test.Assert<std::vector<double> >(myVec.cbegin());
return 0;
}
Try out this once.
The following code is compiled ok using clang.
#include <iostream>
#include <vector>
class Test
{
public:
template <typename T>
void Assert(typename std::vector<T>::const_iterator it)
{
std::cout << *it << std::endl;
}
};
int main(int argc, char* argv[])
{
Test test;
std::vector<double> myVec;
myVec.push_back(2.0f);
test.Assert<double>(myVec.cbegin()); // call Assert in this way.
return 0;
}
The outputs:
$ ./a.out
2
The compiler's version:
$ clang++ -v
Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM
3.6.0svn) Target: x86_64-apple-darwin14.1.0 Thread model: posix
I want to create a hash function that is supported by template types T and E.
I use:
namespace std {
namespace tr1 {
template<class T, class E> struct hash<my_class<T, E> >
{
public:
size_t operator()(const my_class<T, E>& k) const
{
return ((hash<T>()(k.a()))^ (hash<E>()(k.x()) << 1) >> 1);
}
};
}
}
but I get errors such as:
In file included from g.cpp:1:
g.h:35: error: ‘hash’ is not a template
g.h:36: error: explicit specialization of non-template ‘hash’
g.h:76: error: ‘hash’ is not a template
g.h:76: error: ‘hash’ is not a template type
Is there no way to specialize a hash function so that it can use a template?
If not, how would I construct a hash function which is based on generic types T and E?
EDIT: Someone answered below, but not quite to my question. I am interested in being able to define a hash function which itself uses generic types. Something like hash< some_class < T > > where T is a generic type.
hash is in namespace std not in namespace std::tr1. See the snippet from en.cppreference.com for an example on how to specialize it (of course you can also partial specialize it):
#include <iostream>
#include <functional>
#include <string>
struct S
{
std::string first_name;
std::string last_name;
};
namespace std
{
template<>
struct hash<S>
{
typedef S argument_type;
typedef std::size_t value_type;
value_type operator()(argument_type const& s) const
{
value_type const h1 ( std::hash<std::string>()(s.first_name) );
value_type const h2 ( std::hash<std::string>()(s.last_name) );
return h1 ^ (h2 << 1);
}
};
}
int main()
{
S s;
s.first_name = "Bender";
s.last_name = "Rodriguez";
std::hash<S> hash_fn;
std::cout << "hash(s) = " << hash_fn(s) << "\n";
}
It's not a good idea to pollute any standard namespace with your own functions. Just put the class in your own namespace and you can do whatever you want. In your case, just make your own class API-compatible with the standard hash. Additionally, there's no problem in calling the standard hash from within your own hash.