Generic way of getting const-qualification of elements from container - c++

I need a generic function that can take either a const or non-const reference to a container, and return the corresponding reference to elements qualified as per the container.
Something along these lines:
template <typename C>
auto get_nth( C& c, int i ) -> /* not-sure-what, but let's call it T */
{
//.... some tricky code here ...
}
I would like to stress that if C expands to
SomeContainer const
then T would be
SomeContainer::const_reference
and otherwise
SomeContainer::reference
I think I can put it together using type traits and mtl if, my question is if there is a shorter, cleaner way.
I'm using C++x11 (obviously) and boost.
Thanks in advance.

I think you are looking for typename C::reference, see 23.2.1 [container.requirements.general] §4.
Oh wait, the above doesn't work if C is already const. But wait, decltype to the rescue!
template <typename C>
auto get_nth( C&& c, int i ) -> decltype(*c.begin())
{
//.... some tricky code here ...
}
If you also want to support C-style arrays which have no begin member function:
#include <iterator>
template <typename C>
auto get_nth( C&& c, int i ) -> decltype(*std::begin(c))
{
//.... some tricky code here ...
}
And the implementation really isn't that tricky:
#include <iterator>
template <typename C>
auto get_nth( C&& c, int i ) -> decltype(*std::begin(c))
{
auto it = std::begin(c);
std::advance(it, i);
return *it;
}
Note that the above solution accepts lvalues and rvalues, but it will always return an lvalue reference. Depending on the client code, this may be a performance concern. Take the following example code:
std::string s = get_nth(std::vector<std::string> { "hello", "world" }, 0);
This will copy the result into s, even though moving it would be perfectly valid (and, of course, faster).
To solve this problem, we need two overloads, one for lvalues and one for rvalues:
#include <iterator>
#include <type_traits>
template <typename C>
auto get_nth( C& c, int i ) -> decltype(*std::begin(c))
{
auto it = std::begin(c);
std::advance(it, i);
return *it;
}
template <typename C>
auto get_nth( C&& c, int i )
-> typename std::enable_if<std::is_rvalue_reference<C&&>::value,
decltype(std::move(*std::begin(c)))>::type
{
auto it = std::begin(c);
std::advance(it, i);
return std::move(*it);
}
Now the result will be moved into s. The enable_if part is necessary because due to reference collapsing rules, C&& can also bind to lvalues, and then the call to initialize s would be ambiguous.

Related

Can I forward (the type of) a generic overloaded function without creating syntax monsters?

This is just an educational question, which could otherwise be avoided entirely by leveraging on tools such as range-v3.
Anyway, consider this example in which a variadic number of containers is passed to a function which returns a tuple of iterators:
#include <tuple>
#include <iostream>
#include <vector>
template <typename ...T>
auto begin(T&... containers) { return std::tuple( begin(containers)... ); }
int main() {
std::vector<int> a{1,2,3};
std::vector<long> b{1000,2000,3000};
auto bg = begin(a,b);
(void) bg;
return 0;
}
Quite a lot of code duplication will be produced by adding the functions for all the other iterator makers (std::[end, cbegin, cend, rbegin, rend]). Therefore I was looking for a way to forward the generic iterator maker function into my function.
I managed to come up with this:
template <auto F, typename ...T>
auto make(T&... containers) { return std::tuple( F(containers)... ); }
// called as this:
auto bg = make<[](auto& c){return std::begin(c);}> (a,b);
...which is more general but comes with an horrendous syntax for the user, Ideally the call should be something like:
auto bg = make<std::begin>(a, b); // or
auto bg = make(std::begin, a, b);
but I have not been able to make these beauties work...
but I have not been able to make these beauties work...
There is no way to make those beauties work currently. The root of it all is that std::begin et al are not functions. They are function templates. Meaning they represent not a single function but an entire family of them. The same is true when you have regular overloads as well. The moment a function name means more than a single function, it can't be passed. An overload set is not a tangible thing we can pass around like a type or a function reference. It's not a callable really.
Wrapping the actual overloaded call into a lambda bypasses the issue entirely. Because a lambda is an object with a type, and those can be passed around just fine. This essentially lifts the overload set up from being a second class citizen. But it comes with boilerplate.
There was a proposal made (p1170) to automatically lift an overload set into a callable, but so far it didn't seem to gain traction. So C++20 doesn't have the means.
As for the boilerplate, we can cut on it if we are willing to employ macros. A simple macro that lifts an overload set correctly can look something like this:
#define LIFT(...) [](auto&& ...args) \
noexcpet(noexcpet(__VA_ARGS__(std::forward<decltype(args)>(args)...))) \
-> decltype(auto) { \
return __VA_ARGS__(std::forward<decltype(args)>(args)...); \
}
Granted, that's a lot of boilerplate by itself, but it handles the exception specification, as well as return type deduction for functions that don't return by value. It also perfect forwards. So while it is indeed fairly ugly, it allows us to write:
auto bg = make(LIFT(std::begin), a, b);
You could wrap those functions within lambdas so you could use it in the next fashion
make(std::begin, a, b);
i.e
template <typename F, typename ...T>
decltype(auto) make(F func, T&... containers) { return std::tuple( func(containers)... ); }
namespace tl {
auto begin = [](auto& c) {
return std::begin(c);
};
}
int main() {
std::vector<int> a{1,2,3};
std::vector<long> b{1000,2000,3000};
auto bg = make(tl::begin, a, b);
(void) bg;
cout << *++std::get<0>(bg) << ' ' << *std::get<1>(bg);
return 0;
}
Depending on your definition of monster, this is easily achievable:
int main()
{
auto v1 = std::vector{1,2,3};
auto v2 = std::vector{4,5,6};
auto begins = std::tie(v1, v2) >> notstd::begin;
auto ends = std::tie(v1, v2) >> notstd::end;
return 0;
}
Here's the boilerplate:
#include <vector>
#include <tuple>
#include <utility>
namespace notstd
{
namespace detail
{
template<class Tuple, class Function, std::size_t...Is>
auto transform_elements(Tuple&& tup, Function f, std::index_sequence<Is...>)
{
return std::make_tuple(f(std::get<Is>(std::forward<Tuple>(tup)))...);
}
}
template<class Tuple, class Transform>
auto operator >> (Tuple&& tup, Transform t)
{
using tuple_type = std::decay_t<Tuple>;
constexpr auto size = std::tuple_size<tuple_type>::value;
return detail::transform_elements(std::forward<Tuple>(tup),
t,
std::make_index_sequence<size>());
}
constexpr auto begin = [](auto&& x)
{
return std::begin(x);
};
constexpr auto end = [](auto&& x)
{
return std::end(x);
};
}
This is by no means a style recommendation.

Universal reference, infer std::list<T> and T

This is a long shot, however I'm trying to infer a universal reference of type std::list<T> for some T.
I have something like this:
// Is the type A the same as B, regardless of const and references
template <typename A, typename B>
struct is_same_kind {
static constexpr auto value = std::is_same_v<std::remove_cv_t<std::remove_reference_t<A>>,
std::remove_cv_t<std::remove_reference_t<B>>>;
};
template <typename A, typename B>
static constexpr auto is_same_kind_v = is_same_kind<A,B>::value;
template<typename L, typename T, typename = std::enable_if_t<is_same_kind_v<L, std::list<T>>>>
T head(L&& l) {
return *std::forward<L>(l).begin();
}
I get an error as the preprocessor is not able to infer T. Maybe there's some nice trick to infer both L&& as universal reference to std::list<T> and the type T from the argument l?
EDIT: Here's how to call it for example:
int main() {
std::cout << head(std::list{1,2,3});
}
I expect to get 1.
Alright, I think I found a way to do this by defaulting to the value_type member. I'm calling remove_reference_t<L> first, as L& :: value_type won't work if it's passed by lvalue reference.
template<typename L, typename T = typename std::remove_reference_t<L>::value_type, typename = std::enable_if_t<is_same_kind_v<L, std::list<T>>>>
T head(L&& l) {
return *std::forward<L>(l).begin();
}
int main() {
std::cout << head(std::list{1,2,3});
}
This is not an answer to your original question, but an important point on the implementation of your function. The way you use the captured value is incorrect. For ints it doesn't matter, because there is nothing to move, but for some types it does matter (especially for those for which L&& makes real sense). For example:
template<typename L>
auto head(L&& l) {
return *std::forward<L>(l).begin();
}
int main() {
std::list<std::unique_ptr<int>> list;
list.push_back(std::make_unique<int>(1));
list.push_back(std::make_unique<int>(2));
auto z = head(std::move(list));
return 0;
}
This code will not even compile. Here one is trying to make a copy of std::unique_ptr because *it is not moved from.
First, there is no much sense in calling begin() on a forwarded value. The only choice a compiler has is to call either begin() or begin() const and this choice does not depend on the value category of l. The resulting iterator is not automatically an std::move_iterator if begin() is called on an rvalue. Second, you should std::move from *it if an rvalue is passed into the function. So, the head(...) function might look like this (using some nice C++17 features):
template<typename L>
auto head(L&& l) {
if constexpr (std::is_rvalue_reference_v<L&&>)
return std::move(*l.begin());
else
return *l.begin();
}
Instead of *l.begin() we could use l.front(). The same arguments still apply.

std algorithms with pointer to member as comparator/"key"

I often find myself using std::sort, std::max_element, and the like with a lambda that simply invokes a member function
std::vector<MyType> vec;
// populate...
auto m = std::max_element(std::begin(vec), std::end(vec),
[](const MyType& a, const MyType& b) { return a.val() < b.val()})
this feels like a waste of characters and a loss of clarity. I'm aware that I could write another function/callable and pass a function pointer/callable object to these algorithm functions, but I often need to do this sort-by just once in a program and it doesn't strike me as a good way of addressing the problem. What I want to do, ideally is say:
auto m = std::max_element(std::begin(vec), std::end(vec), &MyType::val);
and have the objects be sorted by their val()s. Is there some part of the stdlib I'm overlooking that could assist me with this? or another simple way of doing it? I'd like to make what this is sorting or searching by as obvious as possible.
I'm aware that just &MyType::val isn't enough, I am looking for something that could perhaps wrap it, or provide a similar functionality without obscurring the meaning.
You can use std::mem_fn (or std::tr1::mem_fn)
int main()
{
std::vector<MyType> vec;
auto m = std::max_element(std::begin(vec), std::end(vec), compare_by(std::mem_fn(&MyType::field)));
}
Of course, this assumes that you have a utility like compare_by in your toolbox (as you should :)):
template <typename F>
struct CompareBy {
explicit CompareBy(F&& f) : f(std::forward<F>(f)) {}
template <typename U, typename V>
bool operator()(U const& u, V const& v) const {
return f(u) < f(v);
}
private:
F f;
};
template <typename F>
CompareBy<F> compare_by(F&& f) { return CompareBy<F>(std::forward<F>(f)); }
See it Live On Coliru
You can do it without introducing any new functions (templated or not).
Just use bind and std::less
auto m = std::max_element(vec.begin(), vec.end(),
bind(less<>(), bind(&MyType::val, _1), bind(&MyType::val, _2)));
A templated comparator could help you:
template <typename StructureType,
typename MemberType,
MemberType StructureType::*member>
bool comparator(const StructureType& the_first, const StructureType& the_second)
{
return the_first.*member < the_second.*member;
}
http://ideone.com/K8ytav
A bit of type traits magic could certainly allows you to avoid writing the type.
How about once overloading operator< for your custom type? This can be naturally done inside the class (or directly next to it), and then no further argument is required beside the iterators.
Passing your val() function isn't possible, as you must pass a binary operator.
EDIT: Having read the other valuable alternatives (also sehe's nice response), I want to confirm what I already mentioned in the comment below: In my opinion, nothing beats the readibility, locality and also flexibility of a lambda expression (--on the risk of writing some passages twice).
#Ryan Haining: I suggest you to keep it as in your original post.
An improvement on sehes answer, so as to avoid needing to use std::mem_fn would be provide a pointer to member overload for the compare_by function.
example usage
std::sort(std::begin(vec), std::end(vec), compare_by(&MyType::field));
std::sort(std::begin(vec), std::end(vec), compare_by(&MyType::field, std::greater<>{}));
the code to implement
#include <functional> // std::less
#include <utility> // std::move
#include <type_traits> // std::is_invocable_r
// Forward declaration
template<typename R, typename T, typename F = std::less<R>>
auto compare_by(R T::*, F = F{});
// Implementation
namespace detail {
template<typename T, typename F>
struct compare_by_t;
template<typename R, typename T, typename F>
struct compare_by_t<R T::*, F> : private F
{
compare_by_t(F&& f, R T::*m): F{std::move(f)}, _member{m} {}
R T::* _member;
bool operator()(T const& x, T const& y) const
{
return F::operator()(x .* _member, y .* _member);
}
};
} // detail
template<typename R, typename T, typename F>
auto compare_by(R T::* member, F f)
{
static_assert(std::is_invocable_r<bool, F, R, R>::value);
return detail::compare_by_t<R T::*, F>{ std::move(f), member };
}

Generic equivalent to std function objects

Is there any function objects in the boost that are generic equivalents to the std::equal_to, std::greater etc. family of function objects?
Essentially, std::equal_to should become something like
struct generic_equal_to
{
template <class T, class U>
bool operator()(const T& t, const U& u) const
{
return t == u;
}
};
I can see how the generic versions of std::plus etc. might be trickier due to issues with the return type (though the decltype can solve that). I can't see any possible reason why the std::equal_to function object itself should require a template argument, though.
Surely somewhere in boost or in the STL these versions exist? They are, of course, trivial to write, but I very much dislike duplicating library code, especially for something as apparently trivial as this.
EDIT:
As some context as to why I would want this instead of using lambdas, or another function-object generation method:
I was writing a generic boost::fusion sequence comparison function thusly:
template <class T>
bool sequence_equal(const T& left, const T& right)
{
return fusion::all(
fusion::zip(left, right),
fusion::fused<generic_equal_to>());
}
Note the fusion::fused<generic_equal_to> part, which leads to the isse that you can't practically specify a boost::lambda or boost::phoenix function-object by type. I guess one solution might be decltype:
fusion::fused<decltype(_1 == _2)>()
That seems very awkward though, and might not even work, depending on how boost::lambda or boost::phoenix is implemented - I'm really not sure.
I know you can use fusion::make_fused to get around this whole issue, but then you have to instantiate the function object. The solution I thought of, then, would be a non-template equal_to struct - I called mine generic_equal_to.
I know it's a very trivial problem - after all, make_fused(_1 == _2) will probably inline down to much the same assembly as fused<generic_equal_to>. I just couldn't believe that there was no generic_equal_to function object in boost or in the STL anywhere, hence this question.
I don't think there's anything quite as direct as you're asking for, but there are utilities that not only cover your use-cases, but go beyond. They are Boost.Lambda and Boost.Phoenix (the latter being a more generic successor to the lambda library).
Example using Boost.Lambda for generic equality:
#include <boost/lambda/lambda.hpp>
#include <iomanip>
#include <iostream>
struct foo {};
bool operator==(foo, foo) { return true; }
bool operator==(foo, int) { return false; }
template <typename T, typename U, typename Func>
void f(const T& x, const U& y, Func func)
{
std::cout << func(x, y) << std::endl;
}
int main()
{
using namespace boost::lambda; // for placeholders
std::cout << std::boolalpha;
foo a, b;
int i = 0;
f(a, b, _1 == _2);
f(a, i, _1 == _2);
}
And the same, with Phoenix:
#include <boost/phoenix.hpp>
#include <iomanip>
#include <iostream>
struct foo {};
bool operator==(foo, foo) { return true; }
bool operator==(foo, int) { return false; }
template <typename T, typename U, typename Func>
void f(const T& x, const U& y, Func func)
{
std::cout << func(x, y) << std::endl;
}
int main()
{
using namespace boost::phoenix::arg_names; // for placeholders
std::cout << std::boolalpha;
foo a, b;
int i = 0;
f(a, b, arg1 == arg2);
f(a, i, arg1 == arg2);
}
Each of these can be extended to support the other operators in the obvious way (and more generally, into other expressions). I would personally go with Phoenix, because if you find out you need more functionality than lambda offers you won't end up including both.
Now in C++14 there is std::equal_to<void> (that can be also used as std::equal_to<>)
std::equal_to<> is a specialization of std::equal_to with parameter and return type deduced.
template< class T, class U>
constexpr auto operator()( T&& lhs, U&& rhs ) const
-> decltype(std::forward<T>(lhs) == std::forward<U>(rhs));
Returns the result of equality comparison between lhs and rhs.
Docs

How to emulate C array initialization "int arr[] = { e1, e2, e3, ... }" behaviour with std::array?

(Note: This question is about not having to specify the number of elements and still allow nested types to be directly initialized.)
This question discusses the uses left for a C array like int arr[20];. On his answer, #James Kanze shows one of the last strongholds of C arrays, it's unique initialization characteristics:
int arr[] = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 };
We don't have to specify the number of elements, hooray! Now iterate over it with the C++11 functions std::begin and std::end from <iterator> (or your own variants) and you never need to even think of its size.
Now, are there any (possibly TMP) ways to achieve the same with std::array? Use of macros allowed to make it look nicer. :)
??? std_array = { "here", "be", "elements" };
Edit: Intermediate version, compiled from various answers, looks like this:
#include <array>
#include <utility>
template<class T, class... Tail, class Elem = typename std::decay<T>::type>
std::array<Elem,1+sizeof...(Tail)> make_array(T&& head, Tail&&... values)
{
return { std::forward<T>(head), std::forward<Tail>(values)... };
}
// in code
auto std_array = make_array(1,2,3,4,5);
And employs all kind of cool C++11 stuff:
Variadic Templates
sizeof...
rvalue references
perfect forwarding
std::array, of course
uniform initialization
omitting the return type with uniform initialization
type inference (auto)
And an example can be found here.
However, as #Johannes points out in the comment on #Xaade's answer, you can't initialize nested types with such a function. Example:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
??? std_array = { {1,2}, {3,4} };
Also, the number of initializers is limited to the number of function and template arguments supported by the implementation.
Best I can think of is:
template<class T, class... Tail>
auto make_array(T head, Tail... tail) -> std::array<T, 1 + sizeof...(Tail)>
{
std::array<T, 1 + sizeof...(Tail)> a = { head, tail ... };
return a;
}
auto a = make_array(1, 2, 3);
However, this requires the compiler to do NRVO, and then also skip the copy of returned value (which is also legal but not required). In practice, I would expect any C++ compiler to be able to optimize that such that it's as fast as direct initialization.
I'd expect a simple make_array.
template<typename ret, typename... T> std::array<ret, sizeof...(T)> make_array(T&&... refs) {
// return std::array<ret, sizeof...(T)>{ { std::forward<T>(refs)... } };
return { std::forward<T>(refs)... };
}
Combining a few ideas from previous posts, here's a solution that works even for nested constructions (tested in GCC4.6):
template <typename T, typename ...Args>
std::array<T, sizeof...(Args) + 1> make_array(T && t, Args &&... args)
{
static_assert(all_same<T, Args...>::value, "make_array() requires all arguments to be of the same type."); // edited in
return std::array<T, sizeof...(Args) + 1>{ std::forward<T>(t), std::forward<Args>(args)...};
}
Strangely, can cannot make the return value an rvalue reference, that would not work for nested constructions. Anyway, here's a test:
auto q = make_array(make_array(make_array(std::string("Cat1"), std::string("Dog1")), make_array(std::string("Mouse1"), std::string("Rat1"))),
make_array(make_array(std::string("Cat2"), std::string("Dog2")), make_array(std::string("Mouse2"), std::string("Rat2"))),
make_array(make_array(std::string("Cat3"), std::string("Dog3")), make_array(std::string("Mouse3"), std::string("Rat3"))),
make_array(make_array(std::string("Cat4"), std::string("Dog4")), make_array(std::string("Mouse4"), std::string("Rat4")))
);
std::cout << q << std::endl;
// produces: [[[Cat1, Dog1], [Mouse1, Rat1]], [[Cat2, Dog2], [Mouse2, Rat2]], [[Cat3, Dog3], [Mouse3, Rat3]], [[Cat4, Dog4], [Mouse4, Rat4]]]
(For the last output I'm using my pretty-printer.)
Actually, let us improve the type safety of this construction. We definitely need all types to be the same. One way is to add a static assertion, which I've edited in above. The other way is to only enable make_array when the types are the same, like so:
template <typename T, typename ...Args>
typename std::enable_if<all_same<T, Args...>::value, std::array<T, sizeof...(Args) + 1>>::type
make_array(T && t, Args &&... args)
{
return std::array<T, sizeof...(Args) + 1> { std::forward<T>(t), std::forward<Args>(args)...};
}
Either way, you will need the variadic all_same<Args...> type trait. Here it is, generalizing from std::is_same<S, T> (note that decaying is important to allow mixing of T, T&, T const & etc.):
template <typename ...Args> struct all_same { static const bool value = false; };
template <typename S, typename T, typename ...Args> struct all_same<S, T, Args...>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value && all_same<T, Args...>::value;
};
template <typename S, typename T> struct all_same<S, T>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value;
};
template <typename T> struct all_same<T> { static const bool value = true; };
Note that make_array() returns by copy-of-temporary, which the compiler (with sufficient optimisation flags!) is allowed to treat as an rvalue or otherwise optimize away, and std::array is an aggregate type, so the compiler is free to pick the best possible construction method.
Finally, note that you cannot avoid copy/move construction when make_array sets up the initializer. So std::array<Foo,2> x{Foo(1), Foo(2)}; has no copy/move, but auto x = make_array(Foo(1), Foo(2)); has two copy/moves as the arguments are forwarded to make_array. I don't think you can improve on that, because you can't pass a variadic initializer list lexically to the helper and deduce type and size -- if the preprocessor had a sizeof... function for variadic arguments, perhaps that could be done, but not within the core language.
Using trailing return syntax make_array can be further simplified
#include <array>
#include <type_traits>
#include <utility>
template <typename... T>
auto make_array(T&&... t)
-> std::array<std::common_type_t<T...>, sizeof...(t)>
{
return {std::forward<T>(t)...};
}
int main()
{
auto arr = make_array(1, 2, 3, 4, 5);
return 0;
}
Unfortunatelly for aggregate classes it requires explicit type specification
/*
struct Foo
{
int a, b;
}; */
auto arr = make_array(Foo{1, 2}, Foo{3, 4}, Foo{5, 6});
EDIT No longer relevant:
In fact this make_array implementation is listed in sizeof... operator
The code below introduces undefined behavior as per [namespace.std]/4.4
4.4 The behavior of a C++ program is undefined if it declares a deduction guide for any standard library class template.
# c++17 version
Thanks to template argument deduction for class templates proposal we can use deduction guides to get rid of make_array helper
#include <array>
namespace std
{
template <typename... T> array(T... t)
-> array<std::common_type_t<T...>, sizeof...(t)>;
}
int main()
{
std::array a{1, 2, 3, 4};
return 0;
}
Compiled with -std=c++1z flag under x86-64 gcc 7.0
I know it's been quite some time since this question was asked, but I feel the existing answers still have some shortcomings, so I'd like to propose my slightly modified version. Following are the points that I think some existing answers are missing.
1. No need to rely on RVO
Some answers mention that we need to rely on RVO to return the constructed array. That is not true; we can make use of copy-list-initialization to guarantee there will never be temporaries created. So instead of:
return std::array<Type, …>{values};
we should do:
return {{values}};
2. Make make_array a constexpr function
This allow us to create compile-time constant arrays.
3. No need to check that all arguments are of the same type
First off, if they are not, the compiler will issue a warning or error anyway because list-initialization doesn't allow narrowing. Secondly, even if we really decide to do our own static_assert thing (perhaps to provide better error message), we should still probably compare the arguments' decayed types rather than raw types. For example,
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array<int>(a, b, c); // Will this work?
If we are simply static_asserting that a, b, and c have the same type, then this check will fail, but that probably isn't what we'd expect. Instead, we should compare their std::decay_t<T> types (which are all ints)).
4. Deduce the array value type by decaying the forwarded arguments
This is similar to point 3. Using the same code snippet, but don't specify the value type explicitly this time:
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array(a, b, c); // Will this work?
We probably want to make an array<int, 3>, but the implementations in the existing answers probably all fail to do that. What we can do is, instead of returning a std::array<T, …>, return a std::array<std::decay_t<T>, …>.
There is one disadvantage about this approach: we can't return an array of cv-qualified value type any more. But most of the time, instead of something like an array<const int, …>, we would use a const array<int, …> anyway. There is a trade-off, but I think a reasonable one. The C++17 std::make_optional also takes this approach:
template< class T >
constexpr std::optional<std::decay_t<T>> make_optional( T&& value );
Taking the above points into account, a full working implementation of make_array in C++14 looks like this:
#include <array>
#include <type_traits>
#include <utility>
template<typename T, typename... Ts>
constexpr std::array<std::decay_t<T>, 1 + sizeof... (Ts)>
make_array(T&& t, Ts&&... ts)
noexcept(noexcept(std::is_nothrow_constructible<
std::array<std::decay_t<T>, 1 + sizeof... (Ts)>, T&&, Ts&&...
>::value))
{
return {{std::forward<T>(t), std::forward<Ts>(ts)...}};
}
template<typename T>
constexpr std::array<std::decay_t<T>, 0> make_array() noexcept
{
return {};
}
Usage:
constexpr auto arr = make_array(make_array(1, 2),
make_array(3, 4));
static_assert(arr[1][1] == 4, "!");
C++11 will support this manner of initialization for (most?) std containers.
(Solution by #dyp)
Note: requires C++14 (std::index_sequence). Although one could implement std::index_sequence in C++11.
#include <iostream>
// ---
#include <array>
#include <utility>
template <typename T>
using c_array = T[];
template<typename T, size_t N, size_t... Indices>
constexpr auto make_array(T (&&src)[N], std::index_sequence<Indices...>) {
return std::array<T, N>{{ std::move(src[Indices])... }};
}
template<typename T, size_t N>
constexpr auto make_array(T (&&src)[N]) {
return make_array(std::move(src), std::make_index_sequence<N>{});
}
// ---
struct Point { int x, y; };
std::ostream& operator<< (std::ostream& os, const Point& p) {
return os << "(" << p.x << "," << p.y << ")";
}
int main() {
auto xs = make_array(c_array<Point>{{1,2}, {3,4}, {5,6}, {7,8}});
for (auto&& x : xs) {
std::cout << x << std::endl;
}
return 0;
}
С++17 compact implementation.
template <typename... T>
constexpr auto array_of(T&&... t) {
return std::array{ static_cast<std::common_type_t<T...>>(t)... };
}
While this answer is directed more towards this question, that question was marked as a duplicate of this question. Hence, this answer is posted here.
A particular use that I feel hasn't been fully covered is a situation where you want to obtain a std::array of chars initialized with a rather long string literal but don't want to blow up the enclosing function. There are a couple of ways to go about this.
The following works but requires us to explicitly specify the size of the string literal. This is what we're trying to avoid:
auto const arr = std::array<char const, 12>{"some string"};
One might expect the following to produce the desired result:
auto const arr = std::array{"some string"};
No need to explicitly specify the size of the array during initialization due to template deduction. However, this wont work because arr is now of type std::array<const char*, 1>.
A neat way to go about this is to simply write a new deduction guide for std::array. But keep in mind that some other code could depend on the default behavior of the std::array deduction guide.
namespace std {
template<typename T, auto N>
array(T (&)[N]) -> array<T, N>;
}
With this deduction guide std::array{"some string"}; will be of type std::array<const char, 12>. It is now possible to initialize arr with a string literal that is defined somewhere else without having to specify its size:
namespace {
constexpr auto some_string = std::array{"some string"};
}
auto func() {
auto const arr = some_string;
// ...
}
Alright, but what if we need a modifiable buffer and we want to initialize it with a string literal without specifying its size?
A hacky solution would be to simply apply the std::remove_cv type trait to our new deduction guide. This is not recommended because this will lead to rather surprising results. String literals are of type const char[], so it's expected that our deduction guide attempts to match that.
It seems that a helper function is necessary in this case. With the use of the constexpr specifier, the following function can be executed at compile time:
#include <array>
#include <type_traits>
template<typename T, auto N>
constexpr auto make_buffer(T (&src)[N]) noexcept {
auto tmp = std::array<std::remove_cv_t<T>, N>{};
for (auto idx = decltype(N){}; idx < N; ++idx) {
tmp[idx] = src[idx];
}
return tmp;
}
Making it possible to initialize modifiable std::array-like buffers as such:
namespace {
constexpr auto some_string = make_buffer("some string");
}
auto func() {
auto buff = some_string;
// ...
}
And with C++20, the helper function can even be simplified:
#include <algorithm>
#include <array>
#include <type_traits>
template<typename T, auto N>
constexpr auto make_buffer(T (&src)[N]) noexcept {
std::array<std::remove_cv_t<T>, N> tmp;
std::copy(std::begin(src), std::end(src), std::begin(tmp));
return tmp;
}
C++20 UPDATE: Although there are some excellent answers that provide the desired functionality (such as Gabriel Garcia's answer that uses std::index_sequence), I am adding this answer because the simplest way to do this as of C++20 isn't mentioned: just use std::to_array(). Using the OP's last example of an array of structs:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
auto std_array = std::to_array<A>({ {1,2}, {3,4} });
If std::array is not a constraint and if you have Boost, then take a look at list_of(). This is not exactly like C type array initialization that you want. But close.
Create an array maker type.
It overloads operator, to generate an expression template chaining each element to the previous via references.
Add a finish free function that takes the array maker and generates an array directly from the chain of references.
The syntax should look something like this:
auto arr = finish( make_array<T>->* 1,2,3,4,5 );
It does not permit {} based construction, as only operator= does. If you are willing to use = we can get it to work:
auto arr = finish( make_array<T>= {1}={2}={3}={4}={5} );
or
auto arr = finish( make_array<T>[{1}][{2}[]{3}][{4}][{5}] );
None of these look like good solutions.
Using variardics limits you to your compiler-imposed limit on number of varargs and blocks recursive use of {} for substructures.
In the end, there really isn't a good solution.
What I do is I write my code so it consumes both T[] and std::array data agnostically -- it doesn't care which I feed it. Sometimes this means my forwarding code has to carefully turn [] arrays into std::arrays transparently.
None of the template approaches worked properly for me for arrays of structs, so I crafted this macro solution:
#define make_array(T, ...) \
(std::array<T,sizeof((T[]){ __VA_ARGS__ })/sizeof(T)> {{ __VA_ARGS__ }})
auto a = make_array(int, 1, 2, 3);
struct Foo { int x, y; };
auto b = make_array(Foo,
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
);
Note that although the macro expands its array arguments twice, the first time is inside sizeof, so any side effects in the expression will correctly happen only once.
Have fun!