C++17 allows us to have std::array's template arguments deduced. E.g., I can write
std::array ints = { 1, 2, 3 };
and ints will be of type std::array<int, 3>.
My question is: what if I wanted to specify only the type argument of the array but have the size of the array automatically determined?
The following does not work since it seems like all template arguments have to be specified:
std::array<size_t> sizes = { 1, 2, 3 };
My compiler complains and says: 'std::array': too few template arguments.
Is it possible to have the size of the array determined automatically by template argument deduction? If not, is it possible to create an array by only specifying its type but not its size?
As far as I know, this cannot be done. But a helper method does the trick:
template<typename Type, typename ... T>
constexpr auto makeArray(T&&... t) -> std::array<Type, sizeof...(T)>
{
return {{std::forward<T>(t)...}};
}
Usage example:
const auto container = makeArray<double>(-5.0, 0.0, 5.0, 10.0);
If, I might be so bold as to expand on the Benjamin's answer. Conceptually one does not need always to be explicit about the result type.
template<
typename ... T,
typename CT = std::common_type_t< T... >
>
constexpr auto make_array(T&& ... t)
-> std::array< CT , sizeof...(T)>
{
return { { static_cast<CT>( std::forward<T>(t) ) ...} };
}
Slightly simpler usage
constexpr auto std_arr = make_array(-5.0, 0.0, 5.0, 10.0);
The issue here might be, we are not exactly certain, what type of the std::array we will get. Provided we do care about it.
const auto std_arr = make_array(-5.0, 0, 5.0, 10.0, 42.13f );
Over "here", using MSVC, the latest, I get std array of doubles. If one's bed time reading is ISO C++ standard doc, the one might be sure what type will come out of std::common_type from the tool chain in use.
For the rest of us, literal suffixes will help
constexpr auto sizes = make_array( 1UL, 2UL, 3UL );
But why stop with numbers? One can collect quickly, into an array, instances from the same hierarchy. Not caring about the result type.
{
const auto exceptions = make_array(
std::exception{"SE"},
std::runtime_error{"RE"},
std::logic_error{"LE"} );
}
A bit useless but somewhat weird and wonderful. One thing thou remember: this is compile time situation. Thus, I might prefer:
constexpr auto sizes = std::array{ 1UL, 2UL, 3UL } ;
To answer the OP's question directly.
There is also C++20 std::to_array. For the exact result type and for a bit more comfortable usage:
constexpr auto sizes = std::to_array<size_t>({ 0, 1, 3 });
Enjoy ...
Related
I am storing the variadic arguments to an object constructor inside a std::tuple and so far so good. But when calling an object function using the stored arguments and std::get<>(), I will be thrown a compile-time assertion failure that I simply don't understand. This will happen only when all arguments are not each of a different type.
The compiler error message is:
msvc\14.16.27023\include\tuple(934): error C2338: duplicate type T in
get(tuple)
mcve below:
#include <tuple>
#include <iostream>
using namespace std;
template<class... Args>
struct store_in_tuple {
tuple<Args...> m_tuple_args;
store_in_tuple(Args... args) : m_tuple_args{ args... } {}
void func() {
func_tuple(std::get<Args>(m_tuple_args)...);
}
void func_tuple(Args... args) {}
};
int main(int argc, char** argv) {
store_in_tuple<int, float, double, int> sit1(1, 2.0f, 3.0, 4);
sit1.func(); // <- not ok
store_in_tuple<int, float, double, size_t> sit2(1, 2.0f, 3.0, 4);
sit2.func(); // <- ok
return 0;
}
Why does this happen and is there a workaround ?
The example can be simlpified to the following:
auto t = std::make_tuple(1, 's', 2);
std::get<int>(t);
here, we have a t of a type std::tuple<int, char, int>. std::get can also work with types (alongside indexes), unless you have a duplicate type. std::get<char> will work since there is only one char in the t, but std::get<int> will not work, since it does not know which int to fetch - the 1 or the 2?
This is what's happening here:
void func() {
func_tuple(std::get<Args>(m_tuple_args)...);
}
the std::get<Args>, after expansion, will not work if Args... contain at least one duplicate type, because it will simply not know which one to fetch.
Use C++17 std::apply() to pass all the elements of a tuple to a function.
std::apply([&](auto... x){ func_tuple(x...); }, m_tuple_args);
You insist on staying with C++14?
No problem, cppreference.com shows a short and simple production-quality example-implementation.
Alternatively, you can work directly with std::make_index_sequence to get unique indices instead of duplicated types.
Why does this happen [?]
All goes well when the Args... types are all differents.
You get an error when types collides.
This is because std::get<T>(tuple_val), where T is a type, "Fails to compile unless the tuple has exactly one element of that type" (as you can read in this page). And this seems reasonable to me.
So all goes well with
store_in_tuple<int, float, double, size_t>
because all types are differents, and you get an error from
store_in_tuple<int, float, double, int>
because the two calls to std::get<int>(m_tuple_args) fail.
and is there a workaround ?
Use the numeric version of std::get(), that is ever available, also when types collides.
The usual way in C++14 pass through an helper function with std::index_sequence and std::make_index_sequence (or std::index_sequence_for).
Seems complicated but it's very simple
template <std::size_t ... Is>
void func_helper (std::index_sequence<Is...> const)
{ func_tuple(std::get<Is>(m_tuple_args)...); }
void func ()
{ func_helper(std::index_sequence_for<Args...>{}); }
If you can use C++17, you can use std::apply() that (I suppose) use std::index_sequence under the hood.
Given the following function, taking: a read-only float span (of either dynamic or any static size):
template <long N> void foobar(gsl::span<const float, N> x);
Let's say I have a vector<float>. Passing that as an argument doesn't work, but neither does using gsl::as_span:
std::vector<float> v = {1, 2, 3};
foobar(gsl::as_span(v));
The above does not compile. Apparently gsl::as_span() returns a gsl::span<float>. Besides not understanding why implicit cast to gsl::span<const float> isn't possible, is there a way to force gsl::as_span() to return a read-only span?
Poking around GSL/span.h on the github page you linked to, I found the following overload of as_span that I believe is the one being called here:
template <typename Cont>
constexpr auto as_span(Cont& arr) -> std::enable_if_t<
!details::is_span<std::decay_t<Cont>>::value,
span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>>
{
Expects(arr.size() < PTRDIFF_MAX);
return {arr.data(), narrow_cast<std::ptrdiff_t>(arr.size())};
}
There's lots to digest here, but in particular the return type of this function boils down to span<std::remove_reference<decltype(*arr.data())>, ...>. For your given vector<float> gives span<float,...> because decltype(*arr.data()) is float &. I believe the following should work:
const auto & cv = v;
foobar(as_span(cv));
but can't test it myself unfortunately. Let me know if this works.
as_span is not part of MS/GSL any more, probably because gsl::span was lately aligned to std::span - which you could now use with C++20.
You can use std::as_const to get a const container and create a gsl::span from that (or in your case to use gsl::as_span on it).
foobar(gsl::span<const float>(std::as_const(v)));
Please note that depending on the implementation of foobar it is not necessary to template it. You could also just write
void foobar(gsl::span<const float> x);
Per default the length of the span is dynamic_extent, so spans of any length would be accepted. Of course you would not have the length available during compile time.
If I want to do something like iterate over a tuple, I have to resort to crazy template metaprogramming and template helper specializations. For example, the following program won't work:
#include <iostream>
#include <tuple>
#include <utility>
constexpr auto multiple_return_values()
{
return std::make_tuple(3, 3.14, "pi");
}
template <typename T>
constexpr void foo(T t)
{
for (auto i = 0u; i < std::tuple_size<T>::value; ++i)
{
std::get<i>(t);
}
}
int main()
{
constexpr auto ret = multiple_return_values();
foo(ret);
}
Because i can't be const or we wouldn't be able to implement it. But for loops are a compile-time construct that can be evaluated statically. Compilers are free to remove it, transform it, fold it, unroll it or do whatever they want with it thanks to the as-if rule. But then why can't loops be used in a constexpr manner? There's nothing in this code that needs to be done at "runtime". Compiler optimizations are proof of that.
I know that you could potentially modify i inside the body of the loop, but the compiler can still be able to detect that. Example:
// ...snip...
template <typename T>
constexpr int foo(T t)
{
/* Dead code */
for (auto i = 0u; i < std::tuple_size<T>::value; ++i)
{
}
return 42;
}
int main()
{
constexpr auto ret = multiple_return_values();
/* No error */
std::array<int, foo(ret)> arr;
}
Since std::get<>() is a compile-time construct, unlike std::cout.operator<<, I can't see why it's disallowed.
πάντα ῥεῖ gave a good and useful answer, I would like to mention another issue though with constexpr for.
In C++, at the most fundamental level, all expressions have a type which can be determined statically (at compile-time). There are things like RTTI and boost::any of course, but they are built on top of this framework, and the static type of an expression is an important concept for understanding some of the rules in the standard.
Suppose that you can iterate over a heterogenous container using a fancy for syntax, like this maybe:
std::tuple<int, float, std::string> my_tuple;
for (const auto & x : my_tuple) {
f(x);
}
Here, f is some overloaded function. Clearly, the intended meaning of this is to call different overloads of f for each of the types in the tuple. What this really means is that in the expression f(x), overload resolution has to run three different times. If we play by the current rules of C++, the only way this can make sense is if we basically unroll the loop into three different loop bodies, before we try to figure out what the types of the expressions are.
What if the code is actually
for (const auto & x : my_tuple) {
auto y = f(x);
}
auto is not magic, it doesn't mean "no type info", it means, "deduce the type, please, compiler". But clearly, there really need to be three different types of y in general.
On the other hand, there are tricky issues with this kind of thing -- in C++ the parser needs to be able to know what names are types and what names are templates in order to correctly parse the language. Can the parser be modified to do some loop unrolling of constexpr for loops before all the types are resolved? I don't know but I think it might be nontrivial. Maybe there is a better way...
To avoid this issue, in current versions of C++, people use the visitor pattern. The idea is that you will have an overloaded function or function object and it will be applied to each element of the sequence. Then each overload has its own "body" so there's no ambiguity as to the types or meanings of the variables in them. There are libraries like boost::fusion or boost::hana that let you do iteration over heterogenous sequences using a given vistior -- you would use their mechanism instead of a for-loop.
If you could do constexpr for with just ints, e.g.
for (constexpr i = 0; i < 10; ++i) { ... }
this raises the same difficulty as heterogenous for loop. If you can use i as a template parameter inside the body, then you can make variables that refer to different types in different runs of the loop body, and then it's not clear what the static types of the expressions should be.
So, I'm not sure, but I think there may be some nontrivial technical issues associated with actually adding a constexpr for feature to the language. The visitor pattern / the planned reflection features may end up being less of a headache IMO... who knows.
Let me give another example I just thought of that shows the difficulty involved.
In normal C++, the compiler knows the static type of every variable on the stack, and so it can compute the layout of the stack frame for that function.
You can be sure that the address of a local variable won't change while the function is executing. For instance,
std::array<int, 3> a{{1,2,3}};
for (int i = 0; i < 3; ++i) {
auto x = a[i];
int y = 15;
std::cout << &y << std::endl;
}
In this code, y is a local variable in the body of a for loop. It has a well-defined address throughout this function, and the address printed by the compiler will be the same each time.
What should be the behavior of similar code with constexpr for?
std::tuple<int, long double, std::string> a{};
for (int i = 0; i < 3; ++i) {
auto x = std::get<i>(a);
int y = 15;
std::cout << &y << std::endl;
}
The point is that the type of x is deduced differently in each pass through the loop -- since it has a different type, it may have different size and alignment on the stack. Since y comes after it on the stack, that means that y might change its address on different runs of the loop -- right?
What should be the behavior if a pointer to y is taken in one pass through the loop, and then dereferenced in a later pass? Should it be undefined behavior, even though it would probably be legal in the similar "no-constexpr for" code with std::array showed above?
Should the address of y not be allowed to change? Should the compiler have to pad the address of y so that the largest of the types in the tuple can be accommodated before y? Does that mean that the compiler can't simply unroll the loops and start generating code, but must unroll every instance of the loop before-hand, then collect all of the type information from each of the N instantiations and then find a satisfactory layout?
I think you are better off just using a pack expansion, it's a lot more clear how it is supposed to be implemented by the compiler, and how efficient it's going to be at compile and run time.
Here's a way to do it that does not need too much boilerplate, inspired from http://stackoverflow.com/a/26902803/1495627 :
template<std::size_t N>
struct num { static const constexpr auto value = N; };
template <class F, std::size_t... Is>
void for_(F func, std::index_sequence<Is...>)
{
using expander = int[];
(void)expander{0, ((void)func(num<Is>{}), 0)...};
}
template <std::size_t N, typename F>
void for_(F func)
{
for_(func, std::make_index_sequence<N>());
}
Then you can do :
for_<N>([&] (auto i) {
std::get<i.value>(t); // do stuff
});
If you have a C++17 compiler accessible, it can be simplified to
template <class F, std::size_t... Is>
void for_(F func, std::index_sequence<Is...>)
{
(func(num<Is>{}), ...);
}
In C++20 most of the std::algorithm functions will be constexpr. For example using std::transform, many operations requiring a loop can be done at compile time. Consider this example calculating the factorial of every number in an array at compile time (adapted from Boost.Hana documentation):
#include <array>
#include <algorithm>
constexpr int factorial(int n) {
return n == 0 ? 1 : n * factorial(n - 1);
}
template <typename T, std::size_t N, typename F>
constexpr std::array<std::result_of_t<F(T)>, N>
transform_array(std::array<T, N> array, F f) {
auto array_f = std::array<std::result_of_t<F(T)>, N>{};
// This is a constexpr "loop":
std::transform(array.begin(), array.end(), array_f.begin(), [&f](auto el){return f(el);});
return array_f;
}
int main() {
constexpr std::array<int, 4> ints{{1, 2, 3, 4}};
// This can be done at compile time!
constexpr std::array<int, 4> facts = transform_array(ints, factorial);
static_assert(facts == std::array<int, 4>{{1, 2, 6, 24}}, "");
}
See how the array facts can be computed at compile time using a "loop", i.e. an std::algorithm. At the time of writing this, you need an experimental version of the newest clang or gcc release which you can try out on godbolt.org. But soon C++20 will be fully implemented by all the major compilers in the release versions.
This proposal "Expansion Statements" is interesting and I will provide the link for you to read further explanations.
Click this link
The proposal introduced the syntactic sugar for... as similar to the sizeof... operator. for... loop statement is a compile-time expression which means it has nothing to do in the runtime.
For example:
std::tuple<int, float, char> Tup1 {5, 3.14, 'K'};
for... (auto elem : Tup1) {
std::cout << elem << " ";
}
The compiler will generate the code at the compile-time and this is the equivalence:
std::tuple<int, float, char> Tup1 {5, 3.14, 'K'};
{
auto elem = std::get<0>(Tup1);
std::cout << elem << " ";
}
{
auto elem = std::get<1>(Tup1);
std::cout << elem << " ";
}
{
auto elem = std::get<2>(Tup1);
std::cout << elem << " ";
}
Thus, the expansion statement is not a loop but a repeated version of the loop body as it was said in the document.
Since this proposal isn't in C++'s current version or in the technical specification (if it's accepted). We can use the alternative version from the boost library specifically <boost/hana/for_each.hpp> and use the tuple version of boost from <boost/hana/tuple.hpp>. Click this link.
#include <boost/hana/for_each.hpp>
#include <boost/hana/tuple.hpp>
using namespace boost;
...
hana::tuple<int, std::string, float> Tup1 {5, "one", 5.55};
hana::for_each(Tup1, [](auto&& x){
std::cout << x << " ";
});
// Which will print:
// 5 "one" 5.55
The first argument of boost::hana::for_each must be a foldable container.
Why isn't a for-loop a compile-time expression?
Because a for() loop is used to define runtime control flow in the c++ language.
Generally variadic templates cannot be unpacked within runtime control flow statements in c++.
std::get<i>(t);
cannot be deduced at compile time, since i is a runtime variable.
Use variadic template parameter unpacking instead.
You might also find this post useful (if this not even remarks a duplicate having answers for your question):
iterate over tuple
Here are two examples attempting to replicate a compile-time for loop (which isn't part of the language at this time), using fold expressions and std::integer_sequence. The first example shows a simple assignment in the loop, and the second example shows tuple indexing and uses a lambda with template parameters available in C++20.
For a function with a template parameter, e.g.
template <int n>
constexpr int factorial() {
if constexpr (n == 0) { return 1; }
else { return n * factorial<n - 1>(); }
}
Where we want to loop over the template parameter, like this:
template <int N>
constexpr auto example() {
std::array<int, N> vals{};
for (int i = 0; i < N; ++i) {
vals[i] = factorial<i>(); // this doesn't work
}
return vals;
}
One can do this:
template <int... Is>
constexpr auto get_array(std::integer_sequence<int, Is...> a) -> std::array<int, a.size()> {
std::array<int, a.size()> vals{};
((vals[Is] = factorial<Is>()), ...);
return vals;
}
And then get the result at compile time:
constexpr auto x = get_array(std::make_integer_sequence<int, 5>{});
// x = {1, 1, 2, 6, 24}
Similarly, for a tuple:
constexpr auto multiple_return_values()
{
return std::make_tuple(3, 3.14, "pi");
}
int main(void) {
static constexpr auto ret = multiple_return_values();
constexpr auto for_constexpr = [&]<int... Is>(std::integer_sequence<int, Is...> a) {
((std::get<Is>(ret)), ...); // std::get<i>(t); from the question
return 0;
}
// use it:
constexpr auto w = for_constexpr(std::make_integer_sequence<int, std::tuple_size_v<decltype(ret)>>{});
}
while playing and trying to calculate total size of vector I tried something like
vector<double> vd;
auto area = vd.size()* sizeof (vd::value_type);
//Ive seen Stepanov use area as name for this kind of size, idk if he adds the sizeof vd also to area :)
Unfortunately this doesnt work...
I need to use vector<double>::value_type but that makes code less readable.
Can it be made to work? I dont like sizeof vd.front() because it just looks ugly to write front() for this.
EDIT: decltype variants also fit in what I would call ugly category...
I think decltype can be used:
auto area = vd.size() * sizeof(decltype(vd)::value_type);
as you are using auto I assume C++11 is permitted.
Confirmed with g++ v4.7.2 and clang v3.3.
How about a simple helper function?
template <typename Container>
size_t value_size(const Container &)
{
return sizeof(typename Container::value_type);
}
[...]
vector<double> vd;
auto area = vd.size() * value_size(vd);
You could even overload the function so that it works with other containers such as arrays (of course, you would need to wrap size as well).
Ideally, the entire computation could be wrapped into a generic function:
template <typename Container>
size_t area(const Container &c)
{
return c.size() * sizeof(typename Container::value_type);
}
//possible overload for arrays (not sure it's the best implementation)
template <typename T, size_t N>
size_t area(const T (&arr)[N])
{
return sizeof(arr);
}
[...]
std::vector<double> vd;
auto vd_area = area(vd);
double arr[] = { 1., 2. };
auto arr_area = area(arr);
In C++11, you could use decltype(vd[0]):
auto area = vd.size()* sizeof (decltype(vd[0]));
But in the particular scenario, you could just write this:
auto area = vd.size()* sizeof (vd[0]);
Since the expression inside sizeof (and decltype too) will not be evaluated, both will work even if vd is
empty.
Is it possible to create a template function that takes a variable number of arguments, for example, in this Vector< T, C > class constructor:
template < typename T, uint C >
Vector< T, C >::Vector( T, ... )
{
va_list arg_list;
va_start( arg_list, C );
for( uint i = 0; i < C; i++ ) {
m_data[ i ] = va_arg( arg_list, T );
}
va_end( arg_list );
}
This almost works, but if someone calls Vector< double, 3 >( 1, 1, 1 ), only the first argument has the correct value. I suspect that the first parameter is correct because it is cast to a double during the function call, and that the others are interpreted as ints and then the bits are stuffed into a double. Calling Vector< double, 3 >( 1.0, 1.0, 1.0 ) gives the desired results. Is there a preferred way to do something like this?
Alas, right now there's no good way to do this. Most of the Boost packages that need to do something similar use macro tricks to define things like this:
template < typename T >
Vector< T >::Vector( T )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1 )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1, C c2 )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1, C c2, C c3 )
{ ... }
The macros generate some set number (typically around 10) versions, and provide a mechanism to change the max number of parameters before expanding the construction.
Basically, its a real pain which is why C++0x is introducing variable-length template arguments and delegation methods that will let you do this cleanly (and safely). In the meantime you can either do it with macros, or try a C++ compiler that has support for (some of) these new experimental features. GCC is a good one for this.
Be warned though that since C++0x isn't actually out yet, things can still change and your code may not be in sync with the final version of the standard. Plus, even after the standard comes out, there'll be 5 years or so during which many compilers will only partially support the standard, so your code won't be very portable.
This code looks dangerous and I think your analysis on why it isn't working is spot on, there's no way for the compiler to know that when calling:
Vector< double, 3 >( 1, 1, 1 )
the ones should be passed as doubles.
I would change the constructor to something like:
Vector< T, C >::Vector(const T(&data)[C])
instead, and have the user pass the arguments as an array. Another sort of ugly solution would be something like this:
template < typename T, uint C >
Vector< T, C >::Vector(const Vector<T, C - 1>& elements, T extra) {
}
and call it like this (with some typedefs):
Vector3(Vector2(Vector1(1), 1), 1);
You can do what you want, but don't do it, because it's not typesafe. Best pass a vector of T or a pair of iterators containing those values.
template < typename T, uint C >
Vector< T, C >::Vector(int N, ... )
{
assert(N < C && "Overflow!");
va_list arg_list;
va_start(arg_list, N);
for(uint i = 0; i < N; i++) {
m_data[i] = va_arg(arg_list, T);
}
va_end(arg_list);
}
Vector<int> v(3, 1, 2, 3);
This can be better solved, since all the elements are homogeneous typed anyway.
template < typename Iter, uint C >
Vector< T, C >::Vector(Iter begin, Iter end)
{
T *data = m_data;
while(begin != end)
*data++ = *begin++;
}
int values[] = { 1, 2, 3 };
Vector<int> v(values, values + 3);
Of course, you have to make sure there is enough place in m_data.
Does TypeList fit your needs?
Generic Programming:Typelists and Applications
Loki C++ Library
Tiny Template Library: implementing typelist
In C++0x (really should be called C++1x), you can use template varargs to achieve what you want in a typesafe fashion (and you won't even need to specify the number of arguments!). However, in the current version of C++ (ISO C++ 1998 with 2003 amendments), there is no way to accomplish what you want. You can either hold off or do what Boost does, which is use preprocessor macro magic to repeat the definition of the constructor multiple times with different numbers of parameters up to a hard-coded, but large limit. Given that Boost.Preprocessor is kind of complicating, you could just define all of the following yourself:
Vector<T,C>::Vector();
Vector<T,C>::Vector(const T&);
Vector<T,C>::Vector(const T&, const T&);
// ...
Since the above is kind of painful to do by hand, though, you could write a script to generate it.
std::tr1::array (which looks similar to yours) does not define a constructor, and can be initialized as an aggregate (?)
std::tr1::array<int, 10> arr = {{ 1, 2, 3, 4, 5, 6 }};
Also you could check out Boost.Assignment library.
For example the constructor could be
template < typename T, uint C >
template < typename Range >
Vector< T, C >::Vector( const Range& r )
and instances created with
Vector<int, 4> vec(boost::assign::cref_list_of<4>(1)(3)(4)(7));
You can use variadic , variadic means template with variable argument.more
The problem with variable arguments in constructors is :
you need the cdecl calling convention (or another one that can handle varargs)
you cant define cdecl for a constructor (in MSVS)
So the "correct" code (MS) could be :
template < typename T, uint C > __cdecl Vector< T, C >::Vector( T, ... )
but the compiler will say:
illegal calling convention for constructor/destructor (MS C4166)