C++11 for loop in a Template Function - c++

I am trying to write a function which would Print Data on the console. The function is to be templated as it should accept different types of Data.
The code is as shown below:
template<typename DataType>
void PrintData(DataType *X)
{
for (DataType Data : X)
{
cout << Data << "\t";
}
cout << endl;
}
int main()
{
int nArray[7] = { 7, 5, 4, 3, 9, 8, 6 };
double dArray[5] = { 4.3, 2.5, -0.9, 100.2, 3.0 };
PrintData(nArray);
PrintData(dArray);
system("pause");
return EXIT_SUCCESS;
}
I get an error that variable Data is undeclared in the templated function PrintData.
error C2065: 'Data' : undeclared identifier
error C3312: no callable 'begin' function found for type 'double *'
error C3312: no callable 'begin' function found for type 'int *'
error C3312: no callable 'end' function found for type 'double *'
error C3312: no callable 'end' function found for type 'int *'
Any help would be appreciated.
Thanks

Assuming you have included the iostream header file and the using namespace std; directive. Then your problems are:
You should not use DataType *. Your code makes X a pointer, which is different from array. Use DataType const& or DataType&& instead.
You have to include the iterator header file which provides the begin and end function for C-style array.
The following code works for me.
#include <iostream>
#include <iterator>
using namespace std;
template<typename DataType>
void PrintData(DataType const& X)
{
for (auto Data : X)
{
cout << Data << "\t";
}
cout << endl;
}
int main()
{
int nArray[7] = { 7, 5, 4, 3, 9, 8, 6 };
double dArray[5] = { 4.3, 2.5, -0.9, 100.2, 3.0 };
PrintData(nArray);
PrintData(dArray);
return EXIT_SUCCESS;
}
As commented by Igor Tandetnik, you may use template<struct DataType, size_t N> if you want to refer the size of the array.
Update:
With template<struct DataType> and DataType *X, DataType is deduced as int and double, and X is a pointer which is not a container.
With template<struct DataType, size_t N> and DataType (&X)[N], DataType is deduced as int and double, and X is an array which can be used with range-based for loop.
With template<struct DataType> and DataType&& X or DataType const& X, DataType is deduced as int[7] or double[5], and X is an array as well.

The problem is that you're passing the name of the array (e.g., nArray), which is just a pointer to the first element, basically. However, new-style for loops expect something on which you can call begin and end - a range.
The following changes make it work by using a vector instead of an array. Note that there are very few differences otherwise, and, in general, very few reason to use C-style arrays in contemporary C++.
#include <iostream>
#include <vector>
using namespace std;
template<typename DataType>
void PrintData(const DataType &X)
{
for (const auto &Data : X)
{
cout << Data << "\t";
}
cout << endl;
}
int main()
{
vector<int> nArray = { 7, 5, 4, 3, 9, 8, 6 };
vector<double> dArray = { 4.3, 2.5, -0.9, 100.2, 3.0 };
PrintData(nArray);
PrintData(dArray);
system("pause");
return EXIT_SUCCESS;

You have some problems:
1) DataType must be a container. So, try to use:
std::vector<int> nArray = { 7, 5, 4, 3, 9, 8, 6 };
std::vector<double> dArray = { 4.3, 2.5, -0.9, 100.2, 3.0 };
2) You are not going to change the container. It's better to pass container by const reference:
template<typename DataType>
void PrintData(const DataType & X);
3) Change the loop like this:
for (const auto & value : X) {
std::cout << value << "\t";
}
Code example:
#include <iostream>
#include <vector>
template<typename DataType>
void PrintData(const DataType & X) {
for (const auto & value : X) {
std::cout << value << "\t";
}
std::cout << std::endl;
}
int main() {
std::vector<int> nArray = { 7, 5, 4, 3, 9, 8, 6 };
std::vector<double> dArray = { 4.3, 2.5, -0.9, 100.2, 3.0 };
PrintData(nArray);
PrintData(dArray);
return 0;
}

As Igor suggested, If at all you wish to use DataType * then you need to do something like this
#include <iostream>
#include <iterator>
using namespace std;
template <typename DataType, size_t N>
void PrintData(DataType (&X)[N])
{
for (auto i : X)
cout << i << "\t";
cout << endl;
}
int main()
{
int nArray[7] = { 7, 5, 4, 3, 9, 8, 6 };
double dArray[5] = { 4.3, 2.5, -0.9, 100.2, 3.0 };
PrintData(nArray);
PrintData(dArray);
return EXIT_SUCCESS;
}
Output
7 5 4 3 9 8 6
4.3 2.5 -0.9 100.2 3
Explanation:
If you see void PrintData(int* nArray); & void PrintData(int (&nArray)[7] );
are similar declarations, except that seconds one tells where array ends.
Template function
template <typename DataType, size_t N>
void PrintData(DataType (&X)[N])
is deduced as'
void PrintData(int (&nArray)[7] )
You could also write
void PrintData(int (&nArray)[7] )
{
for (auto i : nArray)
cout << i << "\t";
cout << endl;
}

Related

Is there a way to iterate a vector in reverse using for each loop? [duplicate]

Is there a container adapter that would reverse the direction of iterators so I can iterate over a container in reverse with range-based for-loop?
With explicit iterators I would convert this:
for (auto i = c.begin(); i != c.end(); ++i) { ...
into this:
for (auto i = c.rbegin(); i != c.rend(); ++i) { ...
I want to convert this:
for (auto& i: c) { ...
to this:
for (auto& i: std::magic_reverse_adapter(c)) { ...
Is there such a thing or do I have to write it myself?
Actually Boost does have such adaptor: boost::adaptors::reverse.
#include <list>
#include <iostream>
#include <boost/range/adaptor/reversed.hpp>
int main()
{
std::list<int> x { 2, 3, 5, 7, 11, 13, 17, 19 };
for (auto i : boost::adaptors::reverse(x))
std::cout << i << '\n';
for (auto i : x)
std::cout << i << '\n';
}
Actually, in C++14 it can be done with a very few lines of code.
This is a very similar in idea to #Paul's solution. Due to things missing from C++11, that solution is a bit unnecessarily bloated (plus defining in std smells). Thanks to C++14 we can make it a lot more readable.
The key observation is that range-based for-loops work by relying on begin() and end() in order to acquire the range's iterators. Thanks to ADL, one doesn't even need to define their custom begin() and end() in the std:: namespace.
Here is a very simple-sample solution:
// -------------------------------------------------------------------
// --- Reversed iterable
template <typename T>
struct reversion_wrapper { T& iterable; };
template <typename T>
auto begin (reversion_wrapper<T> w) { return std::rbegin(w.iterable); }
template <typename T>
auto end (reversion_wrapper<T> w) { return std::rend(w.iterable); }
template <typename T>
reversion_wrapper<T> reverse (T&& iterable) { return { iterable }; }
This works like a charm, for instance:
template <typename T>
void print_iterable (std::ostream& out, const T& iterable)
{
for (auto&& element: iterable)
out << element << ',';
out << '\n';
}
int main (int, char**)
{
using namespace std;
// on prvalues
print_iterable(cout, reverse(initializer_list<int> { 1, 2, 3, 4, }));
// on const lvalue references
const list<int> ints_list { 1, 2, 3, 4, };
for (auto&& el: reverse(ints_list))
cout << el << ',';
cout << '\n';
// on mutable lvalue references
vector<int> ints_vec { 0, 0, 0, 0, };
size_t i = 0;
for (int& el: reverse(ints_vec))
el += i++;
print_iterable(cout, ints_vec);
print_iterable(cout, reverse(ints_vec));
return 0;
}
prints as expected
4,3,2,1,
4,3,2,1,
3,2,1,0,
0,1,2,3,
NOTE std::rbegin(), std::rend(), and std::make_reverse_iterator() are not yet implemented in GCC-4.9. I write these examples according to the standard, but they would not compile in stable g++. Nevertheless, adding temporary stubs for these three functions is very easy. Here is a sample implementation, definitely not complete but works well enough for most cases:
// --------------------------------------------------
template <typename I>
reverse_iterator<I> make_reverse_iterator (I i)
{
return std::reverse_iterator<I> { i };
}
// --------------------------------------------------
template <typename T>
auto rbegin (T& iterable)
{
return make_reverse_iterator(iterable.end());
}
template <typename T>
auto rend (T& iterable)
{
return make_reverse_iterator(iterable.begin());
}
// const container variants
template <typename T>
auto rbegin (const T& iterable)
{
return make_reverse_iterator(iterable.end());
}
template <typename T>
auto rend (const T& iterable)
{
return make_reverse_iterator(iterable.begin());
}
Got this example from cppreference. It works with:
GCC 10.1+ with flag -std=c++20
#include <ranges>
#include <iostream>
int main()
{
static constexpr auto il = {3, 1, 4, 1, 5, 9};
std::ranges::reverse_view rv {il};
for (int i : rv)
std::cout << i << ' ';
std::cout << '\n';
for(int i : il | std::views::reverse)
std::cout << i << ' ';
}
If you can use range v3 , you can use the reverse range adapter ranges::view::reverse which allows you to view the container in reverse.
A minimal working example:
#include <iostream>
#include <vector>
#include <range/v3/view.hpp>
int main()
{
std::vector<int> intVec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto const& e : ranges::view::reverse(intVec)) {
std::cout << e << " ";
}
std::cout << std::endl;
for (auto const& e : intVec) {
std::cout << e << " ";
}
std::cout << std::endl;
}
See DEMO 1.
Note: As per Eric Niebler, this feature will be available in C++20. This can be used with the <experimental/ranges/range> header. Then the for statement will look like this:
for (auto const& e : view::reverse(intVec)) {
std::cout << e << " ";
}
See DEMO 2
This should work in C++11 without boost:
namespace std {
template<class T>
T begin(std::pair<T, T> p)
{
return p.first;
}
template<class T>
T end(std::pair<T, T> p)
{
return p.second;
}
}
template<class Iterator>
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it)
{
return std::reverse_iterator<Iterator>(it);
}
template<class Range>
std::pair<std::reverse_iterator<decltype(begin(std::declval<Range>()))>, std::reverse_iterator<decltype(begin(std::declval<Range>()))>> make_reverse_range(Range&& r)
{
return std::make_pair(make_reverse_iterator(begin(r)), make_reverse_iterator(end(r)));
}
for(auto x: make_reverse_range(r))
{
...
}
Does this work for you:
#include <iostream>
#include <list>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator_range.hpp>
int main(int argc, char* argv[]){
typedef std::list<int> Nums;
typedef Nums::iterator NumIt;
typedef boost::range_reverse_iterator<Nums>::type RevNumIt;
typedef boost::iterator_range<NumIt> irange_1;
typedef boost::iterator_range<RevNumIt> irange_2;
Nums n = {1, 2, 3, 4, 5, 6, 7, 8};
irange_1 r1 = boost::make_iterator_range( boost::begin(n), boost::end(n) );
irange_2 r2 = boost::make_iterator_range( boost::end(n), boost::begin(n) );
// prints: 1 2 3 4 5 6 7 8
for(auto e : r1)
std::cout << e << ' ';
std::cout << std::endl;
// prints: 8 7 6 5 4 3 2 1
for(auto e : r2)
std::cout << e << ' ';
std::cout << std::endl;
return 0;
}
Sorry but with current C++ (apart from C++20) all these solutions do seem to be inferior to just use index-based for. Nothing here is just "a few lines of code". So, yes: iterate via a simple int-loop. That's the best solution.
template <typename C>
struct reverse_wrapper {
C & c_;
reverse_wrapper(C & c) : c_(c) {}
typename C::reverse_iterator begin() {return c_.rbegin();}
typename C::reverse_iterator end() {return c_.rend(); }
};
template <typename C, size_t N>
struct reverse_wrapper< C[N] >{
C (&c_)[N];
reverse_wrapper( C(&c)[N] ) : c_(c) {}
typename std::reverse_iterator<const C *> begin() { return std::rbegin(c_); }
typename std::reverse_iterator<const C *> end() { return std::rend(c_); }
};
template <typename C>
reverse_wrapper<C> r_wrap(C & c) {
return reverse_wrapper<C>(c);
}
eg:
int main(int argc, const char * argv[]) {
std::vector<int> arr{1, 2, 3, 4, 5};
int arr1[] = {1, 2, 3, 4, 5};
for (auto i : r_wrap(arr)) {
printf("%d ", i);
}
printf("\n");
for (auto i : r_wrap(arr1)) {
printf("%d ", i);
}
printf("\n");
return 0;
}
You could simply use BOOST_REVERSE_FOREACH which iterates backwards. For example, the code
#include <iostream>
#include <boost\foreach.hpp>
int main()
{
int integers[] = { 0, 1, 2, 3, 4 };
BOOST_REVERSE_FOREACH(auto i, integers)
{
std::cout << i << std::endl;
}
return 0;
}
generates the following output:
4
3
2
1
0
If not using C++14, then I find below the simplest solution.
#define METHOD(NAME, ...) auto NAME __VA_ARGS__ -> decltype(m_T.r##NAME) { return m_T.r##NAME; }
template<typename T>
struct Reverse
{
T& m_T;
METHOD(begin());
METHOD(end());
METHOD(begin(), const);
METHOD(end(), const);
};
#undef METHOD
template<typename T>
Reverse<T> MakeReverse (T& t) { return Reverse<T>{t}; }
Demo.
It doesn't work for the containers/data-types (like array), which doesn't have begin/rbegin, end/rend functions.

Sorting template array using std::sort

#include <algorithm>
#include <array>
#include <iostream>
int main() {
std::array<int, 10> s{5, 7, 4, 2, 8, 6, 1, 9, 0, 3};
struct {
bool operator()(int a, int b) const
{
return a < b;
}
} customLess;
std::sort(s.begin(), s.end(), customLess);
for (auto a : s) {
std::cout << a << " ";
}
std::cout << '\n';
return 0;
}
I want to sort the array using std : : sort. The code above works fine, but when I try with template array in my main task (GArr<int, 5> arr{5, 2, 3, 4, 1};) the compiler gives the following
error: no match for 'operator-' (operand types are 'Iterator' and
'Iterator')|
How can I fix it?

no matching function for call to ‘begin(int**&)’

I wrote a c++ program as fllow(3.43.cpp):
#include <iostream>
using std::cout;
using std::endl;
void version_1(int **arr) {
for (const int (&p)[4] : arr) {
for (int q : p) {
cout << q << " ";
}
cout << endl;
}
}
int main() {
int arr[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
version_1(arr);
return 0;
}
Then I compile it by using: gcc my.cpp -std=c++11, there is an error I can not deal with.
Info:
3.43.cpp:6:30: error: no matching function for call to ‘begin(int**&)’
for (const int (&p)[4] : arr) {
^
3.43.cpp:6:30: note: candidates are:
In file included from /usr/include/c++/4.8.2/bits/basic_string.h:42:0,
from /usr/include/c++/4.8.2/string:52,
from /usr/include/c++/4.8.2/bits/locale_classes.h:40,
from /usr/include/c++/4.8.2/bits/ios_base.h:41,
from /usr/include/c++/4.8.2/ios:42,
from /usr/include/c++/4.8.2/ostream:38,
from /usr/include/c++/4.8.2/iostream:39,
from 3.43.cpp:1:
/usr/include/c++/4.8.2/initializer_list:89:5: note: template<class _Tp> constexpr const _Tp* std::begin(std::initializer_list<_Tp>)
begin(initializer_list<_Tp> __ils) noexcept
I search it in google, but not find similar answer.
Since arr is just a pointer, there's no way to deduce how big it is. But, since you are actually passing in a real array, you can just template your function on its dimensions so you take the actual array by reference rather than having it decay to a pointer:
template <size_t X, size_t Y>
void foo(const int (&arr)[X][Y])
{
std::cout << "Dimensions are: " << X << "x" << Y << std::endl;
for (const int (&row)[Y] : arr) {
for (int val : row) {
std::cout << val << ' ';
}
std::cout << std::endl;
}
}
int main() {
int arr[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
foo(arr);
}
std::begin() and std::end() won't work for pointers. They only work for arrays. If you want to deduce the size of your array you'll need to pass it as a reference your function:
#include <cstddef>
template <std::size_t A, std::size_t B>
void version_1(int (&arr)[B][A]) {
for (const int (&p)[A] : arr) {
for (int q : p) {
cout << q << " ";
}
cout << '\n';
}
}
Pointers are not the same as arrays. To be able to use range based for, your container must support std::begin and std::end. Standard C arrays can be used, but not pointers.

C++11 reverse range-based for-loop

Is there a container adapter that would reverse the direction of iterators so I can iterate over a container in reverse with range-based for-loop?
With explicit iterators I would convert this:
for (auto i = c.begin(); i != c.end(); ++i) { ...
into this:
for (auto i = c.rbegin(); i != c.rend(); ++i) { ...
I want to convert this:
for (auto& i: c) { ...
to this:
for (auto& i: std::magic_reverse_adapter(c)) { ...
Is there such a thing or do I have to write it myself?
Actually Boost does have such adaptor: boost::adaptors::reverse.
#include <list>
#include <iostream>
#include <boost/range/adaptor/reversed.hpp>
int main()
{
std::list<int> x { 2, 3, 5, 7, 11, 13, 17, 19 };
for (auto i : boost::adaptors::reverse(x))
std::cout << i << '\n';
for (auto i : x)
std::cout << i << '\n';
}
Actually, in C++14 it can be done with a very few lines of code.
This is a very similar in idea to #Paul's solution. Due to things missing from C++11, that solution is a bit unnecessarily bloated (plus defining in std smells). Thanks to C++14 we can make it a lot more readable.
The key observation is that range-based for-loops work by relying on begin() and end() in order to acquire the range's iterators. Thanks to ADL, one doesn't even need to define their custom begin() and end() in the std:: namespace.
Here is a very simple-sample solution:
// -------------------------------------------------------------------
// --- Reversed iterable
template <typename T>
struct reversion_wrapper { T& iterable; };
template <typename T>
auto begin (reversion_wrapper<T> w) { return std::rbegin(w.iterable); }
template <typename T>
auto end (reversion_wrapper<T> w) { return std::rend(w.iterable); }
template <typename T>
reversion_wrapper<T> reverse (T&& iterable) { return { iterable }; }
This works like a charm, for instance:
template <typename T>
void print_iterable (std::ostream& out, const T& iterable)
{
for (auto&& element: iterable)
out << element << ',';
out << '\n';
}
int main (int, char**)
{
using namespace std;
// on prvalues
print_iterable(cout, reverse(initializer_list<int> { 1, 2, 3, 4, }));
// on const lvalue references
const list<int> ints_list { 1, 2, 3, 4, };
for (auto&& el: reverse(ints_list))
cout << el << ',';
cout << '\n';
// on mutable lvalue references
vector<int> ints_vec { 0, 0, 0, 0, };
size_t i = 0;
for (int& el: reverse(ints_vec))
el += i++;
print_iterable(cout, ints_vec);
print_iterable(cout, reverse(ints_vec));
return 0;
}
prints as expected
4,3,2,1,
4,3,2,1,
3,2,1,0,
0,1,2,3,
NOTE std::rbegin(), std::rend(), and std::make_reverse_iterator() are not yet implemented in GCC-4.9. I write these examples according to the standard, but they would not compile in stable g++. Nevertheless, adding temporary stubs for these three functions is very easy. Here is a sample implementation, definitely not complete but works well enough for most cases:
// --------------------------------------------------
template <typename I>
reverse_iterator<I> make_reverse_iterator (I i)
{
return std::reverse_iterator<I> { i };
}
// --------------------------------------------------
template <typename T>
auto rbegin (T& iterable)
{
return make_reverse_iterator(iterable.end());
}
template <typename T>
auto rend (T& iterable)
{
return make_reverse_iterator(iterable.begin());
}
// const container variants
template <typename T>
auto rbegin (const T& iterable)
{
return make_reverse_iterator(iterable.end());
}
template <typename T>
auto rend (const T& iterable)
{
return make_reverse_iterator(iterable.begin());
}
Got this example from cppreference. It works with:
GCC 10.1+ with flag -std=c++20
#include <ranges>
#include <iostream>
int main()
{
static constexpr auto il = {3, 1, 4, 1, 5, 9};
std::ranges::reverse_view rv {il};
for (int i : rv)
std::cout << i << ' ';
std::cout << '\n';
for(int i : il | std::views::reverse)
std::cout << i << ' ';
}
If you can use range v3 , you can use the reverse range adapter ranges::view::reverse which allows you to view the container in reverse.
A minimal working example:
#include <iostream>
#include <vector>
#include <range/v3/view.hpp>
int main()
{
std::vector<int> intVec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto const& e : ranges::view::reverse(intVec)) {
std::cout << e << " ";
}
std::cout << std::endl;
for (auto const& e : intVec) {
std::cout << e << " ";
}
std::cout << std::endl;
}
See DEMO 1.
Note: As per Eric Niebler, this feature will be available in C++20. This can be used with the <experimental/ranges/range> header. Then the for statement will look like this:
for (auto const& e : view::reverse(intVec)) {
std::cout << e << " ";
}
See DEMO 2
This should work in C++11 without boost:
namespace std {
template<class T>
T begin(std::pair<T, T> p)
{
return p.first;
}
template<class T>
T end(std::pair<T, T> p)
{
return p.second;
}
}
template<class Iterator>
std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it)
{
return std::reverse_iterator<Iterator>(it);
}
template<class Range>
std::pair<std::reverse_iterator<decltype(begin(std::declval<Range>()))>, std::reverse_iterator<decltype(begin(std::declval<Range>()))>> make_reverse_range(Range&& r)
{
return std::make_pair(make_reverse_iterator(begin(r)), make_reverse_iterator(end(r)));
}
for(auto x: make_reverse_range(r))
{
...
}
Does this work for you:
#include <iostream>
#include <list>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator_range.hpp>
int main(int argc, char* argv[]){
typedef std::list<int> Nums;
typedef Nums::iterator NumIt;
typedef boost::range_reverse_iterator<Nums>::type RevNumIt;
typedef boost::iterator_range<NumIt> irange_1;
typedef boost::iterator_range<RevNumIt> irange_2;
Nums n = {1, 2, 3, 4, 5, 6, 7, 8};
irange_1 r1 = boost::make_iterator_range( boost::begin(n), boost::end(n) );
irange_2 r2 = boost::make_iterator_range( boost::end(n), boost::begin(n) );
// prints: 1 2 3 4 5 6 7 8
for(auto e : r1)
std::cout << e << ' ';
std::cout << std::endl;
// prints: 8 7 6 5 4 3 2 1
for(auto e : r2)
std::cout << e << ' ';
std::cout << std::endl;
return 0;
}
Sorry but with current C++ (apart from C++20) all these solutions do seem to be inferior to just use index-based for. Nothing here is just "a few lines of code". So, yes: iterate via a simple int-loop. That's the best solution.
template <typename C>
struct reverse_wrapper {
C & c_;
reverse_wrapper(C & c) : c_(c) {}
typename C::reverse_iterator begin() {return c_.rbegin();}
typename C::reverse_iterator end() {return c_.rend(); }
};
template <typename C, size_t N>
struct reverse_wrapper< C[N] >{
C (&c_)[N];
reverse_wrapper( C(&c)[N] ) : c_(c) {}
typename std::reverse_iterator<const C *> begin() { return std::rbegin(c_); }
typename std::reverse_iterator<const C *> end() { return std::rend(c_); }
};
template <typename C>
reverse_wrapper<C> r_wrap(C & c) {
return reverse_wrapper<C>(c);
}
eg:
int main(int argc, const char * argv[]) {
std::vector<int> arr{1, 2, 3, 4, 5};
int arr1[] = {1, 2, 3, 4, 5};
for (auto i : r_wrap(arr)) {
printf("%d ", i);
}
printf("\n");
for (auto i : r_wrap(arr1)) {
printf("%d ", i);
}
printf("\n");
return 0;
}
You could simply use BOOST_REVERSE_FOREACH which iterates backwards. For example, the code
#include <iostream>
#include <boost\foreach.hpp>
int main()
{
int integers[] = { 0, 1, 2, 3, 4 };
BOOST_REVERSE_FOREACH(auto i, integers)
{
std::cout << i << std::endl;
}
return 0;
}
generates the following output:
4
3
2
1
0
If not using C++14, then I find below the simplest solution.
#define METHOD(NAME, ...) auto NAME __VA_ARGS__ -> decltype(m_T.r##NAME) { return m_T.r##NAME; }
template<typename T>
struct Reverse
{
T& m_T;
METHOD(begin());
METHOD(end());
METHOD(begin(), const);
METHOD(end(), const);
};
#undef METHOD
template<typename T>
Reverse<T> MakeReverse (T& t) { return Reverse<T>{t}; }
Demo.
It doesn't work for the containers/data-types (like array), which doesn't have begin/rbegin, end/rend functions.

Templated static member functions in C++

I've written a simple test program to try to learn how to use template static member functions in C++. The code compiles, but doesn't work right (prints out some garbage). I guess I'm using the right syntax. I've read this or this and some other stuff but still don't know what I'm doing wrong. The code below:
#include <iostream>
using namespace std;
class Util {
public:
Util();
virtual ~Util();
template <typename T> static void printTab(T tab[]);
};
template <typename T>
void Util::printTab(T tab[]) {
for (unsigned int i=0; i<sizeof(tab)/sizeof(tab[0]); i++) {
cout << tab[0] << " ";
}
cout << endl;
}
int main() {
float tabFloat[5] {1, 2, 3, 4, 5};
unsigned char tabChar[3] {1, 2, 3};
Util::printTab(tabFloat);
Util::printTab(tabChar);
return 0;
}
Any hints appreciated.
You need to pass the size as another template argument :
#include <iostream>
using namespace std;
class Util {
public:
Util();
virtual ~Util();
template <typename T,int N> static void printTab(T (&tab)[N])
{
for (int i=0; i<N; i++) {
cout << tab[i] << " ";
}
cout << endl;
}
};
int main() {
float tabFloat[5] {1, 2, 3, 4, 5};
unsigned char tabChar[3] {1, 2, 3};
Util::printTab(tabFloat);
Util::printTab(tabChar);
}
sizeof(tab) is the size of a T*, it will not return the size of the whole array. You need to pass that in yourself as another argument to the function. See here for an explanation and another potential workaround: When a function has a specific-size array parameter, why is it replaced with a pointer?
Note that the second printTab will not output readable characters. If you want to see something printed out, try with:
unsigned char tabChar[3] {'1', '2', '3'};
How about trying, you need to send the size of the array when calling a function:
#include <iostream>
using namespace std;
class Util {
public:
Util();
virtual ~Util();
template <typename T> static void printTab(T tab[], size_t sz);
};
template <typename T>
void Util::printTab(T tab[], size_t sz) {
for (unsigned int i=0; i<sz; i++) {
cout << tab[i] << " ";
}
cout << endl;
}
int main() {
float tabFloat[5] {1, 2, 3, 4, 5};
unsigned char tabChar[3] {1, 2, 3};
Util::printTab(tabFloat, sizeof(tabFloat)/sizeof(float));
Util::printTab(tabChar, sizeof(tabChar)/sizeof(char));
return 0;
}
I'd pass the number of elements of T as a function argument or use a STD container such as a Vector.
Your for loop is just printing the first element tab[0] not tab[i]
Your initialization of tabFloat and tabChar are missing =
float tabFloat[5] {1, 2, 3, 4, 5};
unsigned char tabChar[3] {1, 2, 3};
(also I'd use 65, 66, 67 instead of 1,2,3 for console readability in your testing)
float tabFloat[5] = {1, 2, 3, 4, 5};
unsigned char tabChar[3] = { 65, 66, 67};