I've been trying to write some shorter code that still uses std::array but I'm not sure how to get the deduction to work in my favor.
Is there a way to write this that is equally as short(readable) as the vector version?
#include <array>
#include <iostream>
#include <vector>
template <std::size_t SIZE>
void afunc(const std::array<std::string_view, SIZE> &v)
{
for(int i=0; i<10; i++)
{
std::cout << v[rand()%SIZE];
}
}
void vfunc(const std::vector<std::string_view> &v)
{
for(int i=0; i<10; i++)
{
std::cout << v[rand()%v.size()];
}
}
using namespace std::string_view_literals;
int main()
{
std::array<std::string_view, 2> works = {"1","2"};
afunc(works);// verbose
//afunc({ "1","2" }); // 1st fail
//afunc({ "1"sv,"2"sv }); // 2nd fail
//afunc(std::array{ "3","4" });// 3rd fail
afunc(std::array{ "3"sv,"4"sv, "test"sv,"other"sv });// works but verbose
vfunc({"5","6", "7"});// vectors are easy to code with
return 0;
}
std::array and std::vector have the same size() and operator[] members, so you could roll your 2 functions into 1 function:
template <typename Container>
void func(const Container &c)
{
for(int i = 0; i < 10; ++i)
{
std::cout << c[rand() % c.size()];
}
}
std::array<std::string_view, 2> a{"1","2"};
func(a);
std::vector<std::string_view> v{"1","2"};
func(v);
func<std::array<std::string_view, 2>>({"1","2"});
func<std::vector<std::string_view>>({"1","2"});
Demo
But, as you can see, that can be too verbose for your taste. But, you can support braced lists as a parameter without having to specify the template argument explicitly, by defining an extra function that accepts std::initializer_list as a parameter, and then the compiler can deduce that parameter for you.
But, std::initializer_list doesn't have operator[], so you would have to change your main function logic to use random-access iterators instead, if you intend to share common code between the functions, eg:
template <typename Iter>
void func(Iter begin, Iter end, std::random_access_iterator_tag)
{
size_t size = end - begin;
for(int i = 0; i < 10; ++i)
{
std::cout << *(begin + (std::rand() % size));
}
}
template <typename Iter>
void func(Iter begin, Iter end)
{
func(begin, end,
typename std::iterator_traits<Iter>::iterator_category());
}
template <typename Container>
void func(const Container &c)
{
func(std::begin(c), std::end(c));
}
template <typename T>
void func(const std::initializer_list<T> &l)
{
func(l.begin(), l.end());
}
std::array<std::string_view, 2> a{"1","2"};
func(a);
std::vector<std::string_view> v{"1","2"};
func(v);
func({"1"sv, "2"sv});
Demo
You can't infer a compile-time size from an initializer list parameter, so you need to pass the size explicitly:
template <std::size_t Size>
void afunc(const std::array<std::string_view, Size> &v)
{ ... }
int main()
{
afunc<2>({ "1", "2" }); // ok
}
Your vfunc for std::vector doesn't need to deduce a compile-time size, which is why this limitation only applies to template argument deduction for std::array in afunc.
Found out how to get C style arrays to work as an option. When I had tried this before my reference was not const thus would not compile with a temporary. Not as good as arrays for the lack of iterators but still straightforward
template<size_t N>
void carr_try(std::string_view const(&a)[N])
{
for(int i=0; i<10; i++)
{
std::cout << a[rand()%N];
}
}
// for illustrative purposes of how to NOT write this routine
template<size_t N>
void nonconst(std::string_view (&a)[N])
{
for(int i=0; i<10; i++)
{
std::cout << a[rand()%N];
}
}
int main()
{
carr_try({ "1", "2" }); // ok
//nonconst({"1","2"});// not ok
std::string_view temp[]={"3","4"};
nonconst(temp);// is fine but long
}
Related
I need to create a constexpr template function to sum container, and pass the static assert. This is my code, but I have to questions.
I recived this error: assignment of read-only reference ‘res’.
Is the sum function written correctly?
#include <iostream>
#include <array>
using namespace std;
template <typename T>
constexpr auto Sum(const T& arr)
{
using arrType = decltype(*arr.begin());
arrType res = 0;
for (auto it = arr.begin(); it != arr.end(); it++) {
res += *it;
}
return res;
}
int main()
{
constexpr array<int, 3> base{5, 2, 0};
static_assert(Sum(base) == 7);
return 0;
}
Your code doesn't compile because since arr is const reference, decltype(*arr.begin()) is const int. So your res is const and you can't assign to it in your loop.
To fix only that error, you should use std::decay, i.e:
using arrType = std::decay_t<decltype(*arr.begin())>;
You don't specify what you mean by "container". If you mean the standard library's definition (which is supported by all STL containers), you can use value_type instead and also further simplify your code:
template <typename T>
constexpr auto Sum(const T& arr) {
typename T::value_type result{};
for (auto const& x : arr) {
result += x;
}
return result;
}
If you have C++20, you can also use std::accumulate:
template <typename T>
constexpr auto Sum(const T& arr)
{
return std::accumulate(arr.begin(), arr.end(),
typename T::value_type{});
}
The problem lies with decltype(*arr.begin()), which gives const U& if U represents the type of the elements of the container arr.
Applying std::remove_reference and std::remove_cv makes it compile.
#include <iostream>
#include <type_traits>
#include <array>
using namespace std;
template <typename T>
constexpr auto Sum(const T& arr)
{
using arrType = remove_cv_t<remove_reference_t<decltype(*arr.begin())>>;
arrType res = 0;
for (auto it = arr.begin(); it != arr.end(); it++) {
res += *it;
}
return res;
}
int main()
{
constexpr array<int, 3> base{5, 2, 0};
static_assert(Sum(base) == 7);
return 0;
}
This question already has answers here:
How to avoid decay with template parameter deduction
(2 answers)
Closed 3 years ago.
I have this code:
template <typename T, ::std::size_t size>
using ary_t = T[size];
template <typename T, ::std::size_t size>
constexpr int call_me(ary_t<T const, size> &a)
{
int total = 10;
for (::std::size_t i = 0; i < size; ++i) {
total += a[i];
}
return total;
}
template <typename T>
constexpr int call_me(T const *a)
{
int total = 0;
for (int i = 0; a[i]; ++i) {
total += a[i];
}
return total;
}
#if 0
int t1()
{
return call_me("a test");
}
#endif
int t2()
{
char const * const s = "a test";
return call_me(s);
}
and it works, but when remove the #if 0 section around t1 it fails to compile because of an ambiguity in which template to use. Is there any way to force the array version of call_me to be used preferentially?
I've tried a number of different tricks to make this work. I've tried adding , int... to the template argument list for the pointer version. I've tried removing the const. I've tried both. I've even tried making the pointer version into a C-style varargs function (aka int call_me(T const *a, ...)). Nothing seems to work.
I'd be happy with an answer that requires what is currently believed will make it into C++2a.
There's an easy workaround:
template <typename T>
constexpr int call_me(T&& arg) {
if constexpr(std::is_pointer_v<std::remove_reference_t<T>>) {
return call_me_pointer(arg);
} else {
return call_me_array(arg);
}
}
If you accept to add a level of indirection, you can add an unused parameter to give the precedence to the array version.
I mean
template <typename T, std::size_t size>
constexpr int call_me_helper (ary_t<T, size> &a, int)
{
int total = 10;
for (std::size_t i = 0; i < size; ++i) {
total += a[i];
}
return total;
}
template <typename T>
constexpr int call_me_helper (T const * a, long)
{
int total = 0;
for (int i = 0; a[i]; ++i) {
total += a[i];
}
return total;
}
template <typename T>
constexpr int call_me (T const & a)
{ return call_me_helper(a, 0); }
I suggest you achieve the same effect by using a span:
What is a "span" and when should I use one?
You just replace the array reference with a fixed-size span:
#include <cstddef>
#include <gsl/span>
template <typename T, std::size_t size>
constexpr int call_me(gsl::span<T const, size> a)
{
int total = 10;
for (std::size_t i = 0; i < size; ++i) {
total += a[i];
}
return total;
}
And there's no ambiguity. What's even nicer is that now you can use standard-library algorithms on containers:
#include <numeric>
template <typename T, std::size_t size>
constexpr int call_me(gsl::span<T const, size> a)
{
return std::accumulate(a.begin(), a.end(), 10);
}
Writing an interface, I have to convert instances of std::vector<double> to boost::array<double,N>. Each time, by construction (without bug), I'm sure the vector has the right size.
Here is an example of what I'm currently doing (I have about 100 such functions to write in the interface) :
double foo1(const std::vector<double>& x)
{
assert(x.size() == 4);
boost::array<double,4> x_;
for(std::size_t i = 0; i < 4; ++i)
x_[i] = x[i];
return foo1_(x_);
}
double foo2(const std::vector<double>& x)
{
assert(x.size() == 6);
boost::array<double,6> x_;
for(std::size_t i = 0; i < 6; ++i)
x_[i] = x[i];
return foo2_(x_);
}
Is there a shorter way to do that ?
Note 1: I don't use C++11 for compatibility reasons.
Note 2: I added tag stdarray because it can maybe help even if it's a C++11 tag.
Something like this:
#include <boost/array.hpp>
#include <vector>
#include <boost/range/algorithm.hpp>
#include <iostream>
#include <cassert>
template<size_t Size, class Container>
boost::array<typename Container::value_type, Size> as_array(const Container &cont)
{
assert(cont.size() == Size);
boost::array<typename Container::value_type, Size> result;
boost::range::copy(cont, result.begin());
return result;
}
int main()
{
// this is C++11 initialization, but the rest of code is C++03-complient
std::vector<int> v{1, 2, 3};
boost::array<int, 3> a = as_array<3>(v);
boost::range::copy(a, std::ostream_iterator<int>(std::cout,", "));
}
But keep in mind that such a "translation" from a run-time to a compile-time container might be quite dangerous, as its correctness relies on assert, which is eliminated in release mode.
Write a template function:
template<class T,size_t N>
std::array<T,N> convert( const std::vector<T> & v )
{
//assert(v.size() == N);
std::array<T,N> r;
std::copy( v.begin(), v.end(), r.begin() );
return r;
}
then your functions will become one liners:
double foo1(const std::vector<double>& x)
{
return foo1_( convert<double,4>( x ) );
}
live example
You can template over the number of elements (That's what array<> is doing too). If you order the parameters sensibly, you can specify just the size and have double still deduced
template<std::size_t N, typename T>
boost::array<T, N> from_vector(const std::vector<T> & vec)
{
assert(x.size() == N);
boost::array<T, N> res;
std::copy(vec.begin(), vec.end(), res.begin());
return res;
}
Which gets used
double foo1(const std::vector<double> & vec)
{
return foo1_(from_vector<4>(vec));
}
double foo2(const std::vector<double> & vec)
{
return foo2_(from_vector<6>(vec));
}
But a better idea would be to re-implement foo1_ and foo2_ in terms of iterators (or something like gsl::span). You can assert(std::distance(begin, end) == 4) as needed
I am trying to write a simple template function that prints every element of some container, without using for loops. So far, I have
#include <iostream>
#include <vector>
#include <algorithm>
template <typename T> void print_with_space(T x){
std::cout << x << ' ';
}
template <typename T> void print_all(T beg, T end){
std::for_each(beg, end, print_with_space<int>);
std::cout << '\n';
}
int main(){
int a[] = {1, 2, 3};
std::vector<int> v(a, a+3);
print_all(v.begin(), v.end());
return 0;
}
The code compiles and runs, but only because I put print_with_space<int> inside the implementation of print_all. I would like to just have print_with_space there for obvious reasons, but then the code doesn't compile. How do I do this?
You can use:
std::for_each(beg, end, [](const typename T::value_type& value) {
print_with_space(value);
});
T is of type std::vector<>::iterator, which is a RandomAccessIterator. Every RandomAcessIterator has a underlying type, which is exposed by value_type.
So, if you pass std::vector<int>::iterator, std::vector<int>::iterator::value_type would be an int.
Now that you have the type, you can make a lambda, which will get executed for every iteration.
In C++14, you can even do:
//'auto' automatically deduces the type for you
std::for_each(beg, end, [](const auto& value) {
print_with_space(value);
});
Another option:
template <typename T> void print_all(T beg, T end) {
std::for_each(beg, end, print_with_space<decltype(*beg)>);
std::cout << '\n';
}
Alternative for C++03:
#include <iterator>
template <typename T> void print_all(T beg, T end)
{
typedef typename std::iterator_traits<T>::value_type val_t;
std::for_each(beg, end, print_with_space<val_t>);
std::cout << '\n';
}
The most flexible solution, which will work with all versions of c++, is to make print_with_space a function object.
This confers a number of advantages:
no need to specify template type at call site.
no need to fiddle around with manual type deduction.
partial specialisation can be achieved by having the functor defer to a templated free function.
Such as:
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
// basic implementation
template<class T> void impl_print_with_space(const T& x)
{
std::cout << x << ' ';
}
// what about special handling for strings?
template<class C, class Ch, class Alloc>
void impl_print_with_space(const std::basic_string<C, Ch, Alloc>& x)
{
std::cout << std::quoted(x) << ' ';
}
// functor
struct print_with_space
{
template<class T> void operator()(const T& x) const
{
impl_print_with_space(x);
}
};
template <typename Iter> void print_all(Iter beg, Iter end)
{
std::for_each(beg, end, print_with_space());
std::cout << '\n';
}
int main(){
int a[] = {1, 2, 3};
std::vector<int> v(a, a+3);
print_all(v.begin(), v.end());
auto b = std::vector<std::string> { "hello", "world" };
print_all(b.begin(), b.end());
return 0;
}
Is there a way to use the new std::array type polymorphically in the size of the array? That is, if I have a function of the form
void DoSomething(std::array<int, 5>& myArray) {
/* ... */
}
Then is it mathematically well-defined to do the following (even if it's not legal C++ code?)
std::array<int, 10> arr;
DoSomething(arr);
Imof this is mathematically well-defined, is there a way to write std::array such that its array elements are contiguous and this code compiles? The only technique I could think of would be to have some weird template metaprogram where std::array<T, N+1> inherits from std::array<T, N>, but I don't believe that forces the array elements to be contiguous.
Directly? No.
You can, however, use compile-time polymorphism to achieve something very similar, and you can write a reference wrapper that makes it easier to work with in the code:
#include <array>
#include <cstddef>
template <typename T, std::size_t N>
struct ref_array_of_at_least
{
template <std::size_t M>
ref_array_of_at_least(T (&a)[M])
: data_(a)
{
static_assert(M >= N, "Invalid size");
}
template <std::size_t M>
ref_array_of_at_least(std::array<T, M>& a)
: data_(&a[0])
{
static_assert(M >= N, "Invalid size");
}
T* data_;
};
Used as:
void f(ref_array_of_at_least<int, 5>) { }
int main()
{
std::array<int, 5> x;
std::array<int, 6> y;
std::array<int, 4> z;
f(x); // ok
f(y); // ok
f(z); // fail
}
(You'd need to add some operator[] overloads and such to ref_array_of_at_least, and it needs some work to make it const correct, but it's a start that demonstrates the possibility of what you are seeking.)
If this was a requirement, one approach is a conversion operator to the required type:
#include <iostream>
template <typename T, int N>
struct Array
{
Array() { for (int i = 0; i < N; ++i) x[i] = 0; }
template <int N2>
operator Array<T, N2>&()
{
// for safety, static assert that N2 < N...
return reinterpret_cast<Array<T, N2>&>(*this);
}
int size() const { return N; }
T x[N];
friend std::ostream& operator<<(std::ostream& os, const Array& a)
{
os << "[ ";
for (int i = 0; i < N; ++i) os << a.x[i] << ' ';
return os << ']';
}
};
void f(Array<int, 5>& a)
{
a.x[a.size() - 1] = -1;
}
int main()
{
Array<int, 10> a;
std::cout << a << '\n';
f(a);
std::cout << a << '\n';
}
I wouldn't recommend it though: pretty horrid. A more explicit mechanism seems a lot less prone to misuse, as well as being more powerful - something vaguely like:
template <size_t N2>
Array<T,N2>& slice(size_t first_index)
{
return *(Array<T,N2>*)(data() + first_index);
}
// usage...
f(a.slice<5>(3)); // elements 3,4,5,6,7.
(clean up the casting for extra points :-/)
No, but you can fake it:
// Hide this function however you like: "detail" namespace, use "_detail"
// in the name, etc.; since it's not part of the public interface.
void f_detail(int size, int *data) {
use(data, /* up to */ data + size);
}
int const f_min_len = 5;
template<int N>
void f(int (&data)[N]) {
static_assert(N >= f_min_len);
f_detail(N, data);
}
template<int N>
void f(std::array<int, N> &data) {
static_assert(N >= f_min_len);
f_detail(N, &data[0]);
}
This is a complete example, and should work exactly as presented. You'd only have to change the data type from int (or make it a template parameter) and add const as required.