Array class that will accept an braced-init-list and deduce length
up vote
5
down vote
favorite
This has been asked before, but I'm curious to see if anything has changed in newer C++ standards. Any current or future standard is acceptable.
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, with elements stored on the stack, and doesn't require a 'make_array' function.
template<class T, size_t N>
struct Array
T items[N];
;
Array<int> foo = 1, 2, 3 ;
Since initializer_list
is not templated on the size, a constructor using it won't do the job. Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Array foo = 1, 2, 3 ; // Works
Array<int> foo = 1, 2, 3 ; // Doesn't work
Array foo = 1.0, 2.0, 3.0f ; //Doesn't work
A constructor that takes a c-array doesn't appear to work because an initializer_list
won't convert to a c-array.
Is the braced-init-list
to T[N]
that happens in int foo = 1, 2, 3 ;
purely compiler magic that can't be replicated in code?
EDIT: The spirit of this question is about the exact syntax above. No make_array, no extra template argument, explicit item type, no double braces, no dynamic allocations. If a trivial Array requires a bunch of modern C++ tomfoolery and still can't manage to support standard syntax then it's just a bad engineering trade-off in my opinion.
c++ c++17 initializer-list template-deduction c++20
add a comment |
up vote
5
down vote
favorite
This has been asked before, but I'm curious to see if anything has changed in newer C++ standards. Any current or future standard is acceptable.
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, with elements stored on the stack, and doesn't require a 'make_array' function.
template<class T, size_t N>
struct Array
T items[N];
;
Array<int> foo = 1, 2, 3 ;
Since initializer_list
is not templated on the size, a constructor using it won't do the job. Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Array foo = 1, 2, 3 ; // Works
Array<int> foo = 1, 2, 3 ; // Doesn't work
Array foo = 1.0, 2.0, 3.0f ; //Doesn't work
A constructor that takes a c-array doesn't appear to work because an initializer_list
won't convert to a c-array.
Is the braced-init-list
to T[N]
that happens in int foo = 1, 2, 3 ;
purely compiler magic that can't be replicated in code?
EDIT: The spirit of this question is about the exact syntax above. No make_array, no extra template argument, explicit item type, no double braces, no dynamic allocations. If a trivial Array requires a bunch of modern C++ tomfoolery and still can't manage to support standard syntax then it's just a bad engineering trade-off in my opinion.
c++ c++17 initializer-list template-deduction c++20
Maybe you could create something much likestd::array
, but force the deduction guide by using some tag type as the first constructor argument. Would look something likeArray foo = Tag<int>, 1, 2, 3;
. (I haven't thought this all the way through yet...)
– BoBTFish
Nov 8 at 8:21
1
@BoBTFish - interesting idea: should permit to explicit the type, bypassing the problem that you can't explicit a template parameter using template guides; IMHO, you should elaborate it and propose it as an answer.
– max66
Nov 8 at 14:12
So, makingstd::array<int>
work was proposed yesterday at the standards meeting and it got shot down, and for good reasons:std::array<int>
looks like a type and is today. Making it a placeholder would 1) potentially break existing code and 2) you would almost never know whether the type you write is also the type you get.
– Rakete1111
2 days ago
I don't know what 'making it a placeholder' means. I also don't know where type ambiguity makes it into the equation. It boils down to: it should be possible to write a constructor that takes a braced-init-list and has a compile-time known size. I'm not looking to change std::array.
– Adam
2 days ago
add a comment |
up vote
5
down vote
favorite
up vote
5
down vote
favorite
This has been asked before, but I'm curious to see if anything has changed in newer C++ standards. Any current or future standard is acceptable.
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, with elements stored on the stack, and doesn't require a 'make_array' function.
template<class T, size_t N>
struct Array
T items[N];
;
Array<int> foo = 1, 2, 3 ;
Since initializer_list
is not templated on the size, a constructor using it won't do the job. Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Array foo = 1, 2, 3 ; // Works
Array<int> foo = 1, 2, 3 ; // Doesn't work
Array foo = 1.0, 2.0, 3.0f ; //Doesn't work
A constructor that takes a c-array doesn't appear to work because an initializer_list
won't convert to a c-array.
Is the braced-init-list
to T[N]
that happens in int foo = 1, 2, 3 ;
purely compiler magic that can't be replicated in code?
EDIT: The spirit of this question is about the exact syntax above. No make_array, no extra template argument, explicit item type, no double braces, no dynamic allocations. If a trivial Array requires a bunch of modern C++ tomfoolery and still can't manage to support standard syntax then it's just a bad engineering trade-off in my opinion.
c++ c++17 initializer-list template-deduction c++20
This has been asked before, but I'm curious to see if anything has changed in newer C++ standards. Any current or future standard is acceptable.
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, with elements stored on the stack, and doesn't require a 'make_array' function.
template<class T, size_t N>
struct Array
T items[N];
;
Array<int> foo = 1, 2, 3 ;
Since initializer_list
is not templated on the size, a constructor using it won't do the job. Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Array foo = 1, 2, 3 ; // Works
Array<int> foo = 1, 2, 3 ; // Doesn't work
Array foo = 1.0, 2.0, 3.0f ; //Doesn't work
A constructor that takes a c-array doesn't appear to work because an initializer_list
won't convert to a c-array.
Is the braced-init-list
to T[N]
that happens in int foo = 1, 2, 3 ;
purely compiler magic that can't be replicated in code?
EDIT: The spirit of this question is about the exact syntax above. No make_array, no extra template argument, explicit item type, no double braces, no dynamic allocations. If a trivial Array requires a bunch of modern C++ tomfoolery and still can't manage to support standard syntax then it's just a bad engineering trade-off in my opinion.
c++ c++17 initializer-list template-deduction c++20
c++ c++17 initializer-list template-deduction c++20
edited 2 days ago
asked Nov 8 at 8:09
Adam
206513
206513
Maybe you could create something much likestd::array
, but force the deduction guide by using some tag type as the first constructor argument. Would look something likeArray foo = Tag<int>, 1, 2, 3;
. (I haven't thought this all the way through yet...)
– BoBTFish
Nov 8 at 8:21
1
@BoBTFish - interesting idea: should permit to explicit the type, bypassing the problem that you can't explicit a template parameter using template guides; IMHO, you should elaborate it and propose it as an answer.
– max66
Nov 8 at 14:12
So, makingstd::array<int>
work was proposed yesterday at the standards meeting and it got shot down, and for good reasons:std::array<int>
looks like a type and is today. Making it a placeholder would 1) potentially break existing code and 2) you would almost never know whether the type you write is also the type you get.
– Rakete1111
2 days ago
I don't know what 'making it a placeholder' means. I also don't know where type ambiguity makes it into the equation. It boils down to: it should be possible to write a constructor that takes a braced-init-list and has a compile-time known size. I'm not looking to change std::array.
– Adam
2 days ago
add a comment |
Maybe you could create something much likestd::array
, but force the deduction guide by using some tag type as the first constructor argument. Would look something likeArray foo = Tag<int>, 1, 2, 3;
. (I haven't thought this all the way through yet...)
– BoBTFish
Nov 8 at 8:21
1
@BoBTFish - interesting idea: should permit to explicit the type, bypassing the problem that you can't explicit a template parameter using template guides; IMHO, you should elaborate it and propose it as an answer.
– max66
Nov 8 at 14:12
So, makingstd::array<int>
work was proposed yesterday at the standards meeting and it got shot down, and for good reasons:std::array<int>
looks like a type and is today. Making it a placeholder would 1) potentially break existing code and 2) you would almost never know whether the type you write is also the type you get.
– Rakete1111
2 days ago
I don't know what 'making it a placeholder' means. I also don't know where type ambiguity makes it into the equation. It boils down to: it should be possible to write a constructor that takes a braced-init-list and has a compile-time known size. I'm not looking to change std::array.
– Adam
2 days ago
Maybe you could create something much like
std::array
, but force the deduction guide by using some tag type as the first constructor argument. Would look something like Array foo = Tag<int>, 1, 2, 3;
. (I haven't thought this all the way through yet...)– BoBTFish
Nov 8 at 8:21
Maybe you could create something much like
std::array
, but force the deduction guide by using some tag type as the first constructor argument. Would look something like Array foo = Tag<int>, 1, 2, 3;
. (I haven't thought this all the way through yet...)– BoBTFish
Nov 8 at 8:21
1
1
@BoBTFish - interesting idea: should permit to explicit the type, bypassing the problem that you can't explicit a template parameter using template guides; IMHO, you should elaborate it and propose it as an answer.
– max66
Nov 8 at 14:12
@BoBTFish - interesting idea: should permit to explicit the type, bypassing the problem that you can't explicit a template parameter using template guides; IMHO, you should elaborate it and propose it as an answer.
– max66
Nov 8 at 14:12
So, making
std::array<int>
work was proposed yesterday at the standards meeting and it got shot down, and for good reasons: std::array<int>
looks like a type and is today. Making it a placeholder would 1) potentially break existing code and 2) you would almost never know whether the type you write is also the type you get.– Rakete1111
2 days ago
So, making
std::array<int>
work was proposed yesterday at the standards meeting and it got shot down, and for good reasons: std::array<int>
looks like a type and is today. Making it a placeholder would 1) potentially break existing code and 2) you would almost never know whether the type you write is also the type you get.– Rakete1111
2 days ago
I don't know what 'making it a placeholder' means. I also don't know where type ambiguity makes it into the equation. It boils down to: it should be possible to write a constructor that takes a braced-init-list and has a compile-time known size. I'm not looking to change std::array.
– Adam
2 days ago
I don't know what 'making it a placeholder' means. I also don't know where type ambiguity makes it into the equation. It boils down to: it should be possible to write a constructor that takes a braced-init-list and has a compile-time known size. I'm not looking to change std::array.
– Adam
2 days ago
add a comment |
5 Answers
5
active
oldest
votes
up vote
6
down vote
You can work around the need for all the types in the list to be the same by using an explicit deduction guide:
template <class... T>
Array(T&&... t) -> Array<std::common_type_t<T...>, sizeof...(T)>;
Array foo = 1.0, 2.0, 3.0f ; // Deduces Array<double,3u>
This is the closest I've seen to an answer, falling short only in that the array size is not part of the template.
– Steven W. Klassen
Nov 9 at 8:09
@StevenW.Klassen It is a part of the template (a second template parameter). Template parameters of the deduction guide don't match template parameters of the class here.
– HolyBlackCat
2 days ago
add a comment |
up vote
5
down vote
I hate to be the bearer of bad news, but I believe that at the current time (at least as of C++17) the answer to your question is "no" there is no way that would meet all of your given requirements. That said, there are solutions posted here that come close, but they all fall short in one or more of your requirements.
I also suspect that the answer will remain "no" in the near future. Not that I'm a prophet, but the direction of the last few C++ versions would seem to imply that a make_array
solution is more likely to be what is added rather than more direct language support.
Explanation
Allow me to explain why in a little more detail.
First consider the C++17 deduction guides. I won't go into detail on them as they are discussed suitably by other answers on this question. They do come very close to what you are asking but do seem to fall short in one way or the other. (Although the answer by @max66 would seem to meet all your requirements except for the extra brackets. If his syntax in fact works you may want to consider that answer as "close enough".)
Next consider a variatic templates solution. In order to automatically determine N you would need a series of overloaded functions (basically one with a single argument and one that takes a single argument and the rest of the variatic templates). But that would essentially be equivalent to some form of make_array
so it doesn't count either.
Finally, the only other option I could see would be based on an initializer_list. The question is how to determine N from that list. In C++11 this would be obviously impossible since the only access to the size of the list is const
and not constexpr
. However as of C++14 the size()
method is in fact constexpr
so you would think it is at least theoretically possible to get the compiler to deduce N based on that. Unfortunately to do this you would need to make N (a template parameter) default to a value of something in the class constructor (the initializer list). I have been unable to determine any means of doing this in the present form of the language.
How I think a future version could support this
One way to support this would be to follow the examples of other languages and add direct language support, bridging a syntax to certain classes. But this essentially makes some classes "special". Consider the following line in Swift:
let ar = [1, 2, 3, 4]
In this example ar
is an object of the type Array<Int>
. But this is done by direct compiler support, i.e. "Array" is a special case. No matter what you do you could not write a MyArray class that would perform the same way (other than perhaps having MyArray accept an Array as a construction option). It is certainly possible for the C++ standard to be extended to do something similar, but C++ tends to try to avoid those "special" cases. Besides, an argument could be made that
auto ar = make_array(1, 2, 3, 4);
is in fact a clearer representation of the intent than would be something like the following: (suggesting the character 'A' to distinguish this from an initializer list)
auto ar = A 1, 2, 3, 4 ;
Another way, more in line with current C++ syntax, to do this would be to add the N parameter to the initializer_list template class. After all since size()
is now constexpr
the size must be known at compile time, so why not make it available as a template parameter? It could suitably default so that it could rarely be needed, but it would allow classes (both standard ones like std::array
and custom ones like you are proposing) the possibility of tying the N in the Array
template to the N
in the initializer_list
. Then you should be able to write something along the following lines:
template<class T, size_t N>
struct Array
explicit Array(std::initializer_list<N> il);
Of course the trick would be to make this initializer_list
change in a fashion that does not break a lot of existing code.
I suspect the standards committee will not follow either of these paths, but will more likely add the experimental make_array
method. And I'm not convinced that is a bad idea. We are used to make_...
in many other parts of the language, so why not here as well?
1
My answer isn’t make_array, my answer is no there isn’t at the current time. The experimental make_array may be an indication that something is perhaps being considered.
– Steven W. Klassen
Nov 8 at 18:41
That said, there are some techniques being listed here that could yet prove me wrong. (Perhaps the answer is not ‘no’.)
– Steven W. Klassen
Nov 8 at 18:43
1
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - From Review
– Ken Y-N
Nov 8 at 23:48
1
The reviewers have a divided opinion of my post with half saying delete and half saying it is ok. Before going the delete route, I'm going to rephrase my answer and explain in more detail why I believe the correct answer is in fact (and unfortunately) "no".
– Steven W. Klassen
Nov 9 at 8:02
add a comment |
up vote
1
down vote
Here's one approach that allows you to specify the type. We use an extra argument to specify the type, so that we can still use the deduction guide to pick up the size. I don't consider it pretty though:
#include <iostream>
template <typename T>
struct Tag ;
template <typename T, size_t N>
struct Array
T data_[N];
template <typename... U>
Array(Tag<T>, U... u)
: data_static_cast<T>(u)... // cast to shut up narrowing conversions - bad idea??
;
template <typename T, typename... U>
Array(Tag<T>, U...) -> Array<T, sizeof...(U)>;
int main()
Array aTag<double>, 1, 2.0f, 3.0;
for (auto d : a.data_)
std::cout << d << 'n';
This is clearly not a full implementation of such a class, just to illustrate the technique.
1
Nice; I suggest to add, a SFINAE enable check in template signature (something astemplate <typename ... U, typename = std::enable_if_t<N == sizeof...(U)>>
(or maybe is enoughsizeof...(U) <= N
) to avoid that someone can write something asArray<int, 2> aTag<int>, 1, 2, 3;
– max66
Nov 8 at 15:07
add a comment |
up vote
1
down vote
Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Not necessarily.
Yes, you can't explicit the type parameter, but you can decide it according the types of the items.
I imagine two reasonable strategies: (1) the type of the Array
is the type of the first item, following the std::array
way, so writing the following deduction guide
template <typename T, typename ... Us>
Array(T, Us...) -> Array<T, 1u + sizeof...(Us)>;
(but observe that a C++ program where a Us
type is different from T
, for a std::array
, is ill formed) or (2) follows the metalfox's suggestion and select the item's common type
template <typename ... Ts>
Array(Ts...) -> Array<std::common_type_t<Ts...>, sizeof...(Ts)>;
Is the braced-init-list to
T[N]
that happens in intfoo = 1, 2, 3 ;
purely compiler magic that can't be replicated in code?
Are you thinking in a deduction guide as follows ?
template <typename T, std::size_t N>
Array(T const (&)[N]) -> Array<T, N>;
Works but with a couple of drawbacks: (1) you have to add a couple of brackets using it
// added ---V V--- added
Array foo1 = 1, 2, 3 ; // Works
and (2) remain the problem that all items must have the same type
Array foo2 = 1.0, 2.0, 3.0f ; //Doesn't work: incompatible types
or the compiler can't deduce the type T
P.s.: what's wrong with a make_array()
function ?
P.s.2: I suggest to give a look at BoBTFish's answer to see a nice method to bypass the impossibility to explicit a template argument using deduction guides.
add a comment |
up vote
0
down vote
You had asked this:
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, and without a 'make_array' function.
I have worked on an implementation of a class that behaves in the manner you are describing only it has a little bit more complexity to it but still fairly simple, readable, reusable, portable and generic.
I was not able to have a T items
array as a direct member in the class. I had to use a T* items
instead and create an overloaded operator
in the derived class to mimic the behavior of an array. This doesn't mean that there isn't any work around to this as others have shown. I just find that this is one possible solution without having to specify the size of the array.
I use a base class to store the elements from the constructor either by means of an std::initializer_list
or of a variadic constructor
. The class templates themselves are not a variadic template only their constructor's are. The base class stores the values from the initializer_list
or parameter pack
into an std::vector
. The inherited class stores the contents from the vector
into T*
by calling the data()
function of the vector class.
template<typename T>
class ParamPack
protected:
std::vector<T> values_;
size_t size_;
public:
template<typename... U>
ParamPack( U... u ) :
values_ static_cast<T>(u)... ,
size_( sizeof...(U) )
template<typename ... U>
ParamPack( std::initializer_list<std::is_same<T, U...>( U...)> il ) :
values_( il ), size_( il.size() )
std::vector<T>& operator()() return values_;
size_t size() const return size_;
;
template<typename T>
class Array : public ParamPack<T>
private:
T* items_;
public:
template<typename... U>
Array( U... u ) : ParamPack<T>::ParamPack( u... )
items_ = this->values_.data();
template<typename... U>
Array( std::initializer_list<U...> il ) : ParamPack<T>::ParamPack( il )
items_ = this->values_.data();
T& operator( size_t idx )
return items_[idx];
T operator( size_t idx ) const
return items_[idx];
T* data() const return items_;
;
int main()
try Elements: ";
for( auto& v : pp3() )
std::cout << v << " ";
std::cout << "nn";
// Array Examples:
std::cout << "Array<T> Examples:n";
std::cout << "Using Array<T>'s initializer_list Constructorn";
Array<int> arr( il );
for( size_t i = 0; i < arr.size(); i++ )
std::cout << arr[i] << " ";
std::cout << "n";
// Using Variadic Constructor
std::cout << "Using Array<T>'s Variadic Constructorn";
Array<int> testA( 9, 8, 7, 6 );
for( size_t i = 0; i < testA.size(); i++ )
std::cout << testA[i] << " ";
std::cout << 'n';
Array<std::string> testB( "Hello", " World" );
for( size_t i = 0; i < testB.size(); i++ )
std::cout << testB[i] << " ";
std::cout << "nn";
// Using Constructor w/ Initializer List
std::cout << "Using Array<T>'s Variadic Constructor with Initializer Listn";
Array<int> testC( 105, 210, 420 );
for( size_t i = 0; i < testC.size(); i++ )
std::cout << testC[i] << " ";
std::cout << "nn";
// Using Initializer List with =
std::cout << "Using Array<T>'s Initializer List with =n";
Array<int> a = 1, 2, 3, 4 ;
for( size_t i = 0; i < a.size(); i++ )
std::cout << a[i] << " ";
std::cout << 'n';
Array<char> b = 'a', 'b', 'c', 'd' ;
for ( size_t i = 0; i < b.size(); i++ )
std::cout << b[i] << " ";
std::cout << 'n';
Array<double> c = 1.2, 3.4, 4.5, 6.7 ;
for( size_t i = 0; i < c.size(); i++ )
std::cout << c[i] << " ";
std::cout << "nn";
// Using Initializer List directly
std::cout << "Using Array<T>'s Initalizer List directlyn";
Array<uint32_t> a1 3, 6, 9, 12 ;
for( size_t i = 0; i < a1.size(); i++ )
std::cout << a1[i] << " ";
std::cout << "nn";
// Using user defined data type
struct Point
int x_, y_;
Point( int x, int y ) : x_( x ), y_( y )
;
Point p1( 1, 2 ), p2( 3, 4 ), p3( 5, 6 );
// Variadic Constructor
std::cout << "Using Array<T>'s Variadic Consturctor with user data typen";
Array<Point> d1( p1, p2, p3 );
for( size_t i = 0; i < d1.size(); i++ )
std::cout << "(" << d1[i].x_ << "," << d1[i].y_ << ") ";
std::cout << 'n';
// Initializer List Construtor (reversed order)
std::cout << "Using Array<T>'s Initializer List Constructor with user data typen";
Array<Point> d2( p3, p2, p1 );
for( size_t i = 0; i < d2.size(); i++ )
std::cout << "(" << d2[i].x_ << "," << d2[i].y_ << ") ";
std::cout << 'n';
// Initializer List Version = ... p2 first
std::cout << "Using Array<T>'s = Initializer List with user data typen";
Array<Point> d3 = p2, p1, p3 ;
for( size_t i = 0; i < d3.size(); i++ )
std::cout << "(" << d3[i].x_ << "," << d3[i].y_ << ") ";
std::cout << 'n';
// Initializer List Directly p2 first p1 & p3 swapped
std::cout << "Using Array<T>'s Initializer List directly with user data typen";
Array<Point> d4 p2, p3, p1 ;
for( size_t i = 0; i < d4.size(); i++ )
std::cout << "(" << d4[i].x_ << "," << d4[i].y_ << ") ";
std::cout << 'n';
std::initializer_list<Point> ilPoints p1, p2, p3 ;
std::cout << "Using Array<T>'s initializer_list Constructor with user data typen";
Array<Point> d5( ilPoints );
for( size_t i = 0; i < d5.size(); i++ )
std::cout << "(" << d5[i].x_ << "," << d5[i].y_ << ") ";
std::cout << "nn";
// Need a local copy of the vector instead?
std::cout << "Using Array<T>'s base class's operator()() to retrieve vectorn";
std::vector<Point> points = d4(); // using operator()()
for( auto& p : points )
std::cout << "(" << p.x_ << "," << p.y_ << ") ";
std::cout << 'n';
// Need a local copy of the pointer instead?
std::cout << "Using Array<T>'s data() to get the contents of its internal pointern";
Point* pPoint = nullptr;
pPoint = d4.data();
for( size_t i = 0; i < d4.size(); i++ )
std::cout << "(" << pPoint[i].x_ << "," << pPoint[i].y_ << ") ";
std::cout << 'n';
catch( const std::runtime_error& e )
std::cerr << e.what() << 'n';
return EXIT_FAILURE;
return EXIT_SUCCESS;
-Output-
ParamPack<T> Examples:
Using ParamPack<T>'s Variadic Constructor
Size: 4 | Elements: 1 2 3 4
Using ParamPack<T>'s Variadic Constructor with an Initializer List
Size: 4 | Elements: 5 6 7 8
Using ParamPack<T>'s initializer_list Constructor
Size: 4 | Elements: 9 10 11 12
Array<T> Examples:
Using Array<T>'s initializer_list Constructor
9 10 12 12
Using Array<T>'s Variadic Constructor
9 8 7 6
Hello World
Using Array<T>'s Constructor with Initializer List
105 210 420
Using Array<T>'s Initializer List with =
1 2 3 4
a b c d
1.2 3.4 5.6 7.8
Using Array<T>'s Initializer List directly
3 6 9 12
Using Array<T>'s Variadic Constructor with user data type
(1,2) (3,4) (5,6)
Using Array<T>'s Variadic Constructor With Initializer List of user data type
(5,6) (3,4) (1,2)
Using Array<T>'s = Initializer List with user data type
(3,4) (1,2) (5,6)
Using Array<T>'s Initializer List directly with user data type
(3,4) (5,6) (1,2)
Using Array<T>'s initializer_list Constructor with user data type
Using Array<T>'s base class's operator()() to retrieve vector
(3,4) (5,6) (1,2)
Using Array<T>'s data() to get the contents of its internal pointer
(3,4) (5,6) (1,2)
Now this is a little more robust as it has operators that are available from both the parent and child class, from the parent class you can get the stored vector
directly from its operator()()
. From the child class you can index into the child's stored pointer from operator()
and there is a function to return it's size. The template itself does not contain a size_t N
template argument since the size is stored internally in the base class and is determined by the size of its vector. With this I'm treating T* p
as if it was T p[size]
. This class is still not without some limitations.
-Valid Construction-
Array<int> a( 1, 2, 3, 4 ); // Variadic Constructor Okay
Array<int> a( 1,2,3,4 ); // Initializer List Constructor Okay
Array<int> a = 1, 2, 3, 4 ; // Initializer List Okay
Array<int> a 1,2,3,4 ; // Initializer List Okay
-Limitations-
However you have to explicitly
instantiate the template as these will not work as they all generate a compiler error.
Array a( 1,2,3,4 );
Array a( 1,2,3,4 );
Array a = 1, 2, 3, 4 ;
Array a 1,2,3,4 ;
-Note- There maybe more that can be done to make this more efficient, maybe even thread safe or exception safe, but this is just a generalization of the class design.
Please let me know what you think about this:
I appreciate your thoroughness. However, this answer boils down to 'allocate and copy elements into a vector' and it's needlessly complex even for that case. The defining features of an array are no dynamic allocation and a compile time known size.
– Adam
2 days ago
@Adam I know, but the problem with trying to not specific the array's size by inferring it from the amount of the parameters passed into the initializer list's constructor will not work because the size of the array has to be known at compile time. Even though you are calling the constructor on the class, the compiler has to set aside memory for the class and all of its members before it can call its ctor and thus it can not create the array without knowing how many elements of typeT
to reserve for the class's construction.
– Francis Cugler
yesterday
@Adam also your question is a very good question. I posted this answer because I enjoyed the attempt at trying to mimic a C arrays behavior. There are no direct issues with the code above, maybe some room for efficiency, but in the output section I'm kind of wording some of it wrong. Non of the examples are using the 2nd constructor of the two classes. They are all using the 1st constructor which is the variadic version. I'm working on it now to update the main and it's output to make it more clear.
– Francis Cugler
yesterday
add a comment |
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
You can work around the need for all the types in the list to be the same by using an explicit deduction guide:
template <class... T>
Array(T&&... t) -> Array<std::common_type_t<T...>, sizeof...(T)>;
Array foo = 1.0, 2.0, 3.0f ; // Deduces Array<double,3u>
This is the closest I've seen to an answer, falling short only in that the array size is not part of the template.
– Steven W. Klassen
Nov 9 at 8:09
@StevenW.Klassen It is a part of the template (a second template parameter). Template parameters of the deduction guide don't match template parameters of the class here.
– HolyBlackCat
2 days ago
add a comment |
up vote
6
down vote
You can work around the need for all the types in the list to be the same by using an explicit deduction guide:
template <class... T>
Array(T&&... t) -> Array<std::common_type_t<T...>, sizeof...(T)>;
Array foo = 1.0, 2.0, 3.0f ; // Deduces Array<double,3u>
This is the closest I've seen to an answer, falling short only in that the array size is not part of the template.
– Steven W. Klassen
Nov 9 at 8:09
@StevenW.Klassen It is a part of the template (a second template parameter). Template parameters of the deduction guide don't match template parameters of the class here.
– HolyBlackCat
2 days ago
add a comment |
up vote
6
down vote
up vote
6
down vote
You can work around the need for all the types in the list to be the same by using an explicit deduction guide:
template <class... T>
Array(T&&... t) -> Array<std::common_type_t<T...>, sizeof...(T)>;
Array foo = 1.0, 2.0, 3.0f ; // Deduces Array<double,3u>
You can work around the need for all the types in the list to be the same by using an explicit deduction guide:
template <class... T>
Array(T&&... t) -> Array<std::common_type_t<T...>, sizeof...(T)>;
Array foo = 1.0, 2.0, 3.0f ; // Deduces Array<double,3u>
edited Nov 8 at 13:55
answered Nov 8 at 10:21
metalfox
1,500316
1,500316
This is the closest I've seen to an answer, falling short only in that the array size is not part of the template.
– Steven W. Klassen
Nov 9 at 8:09
@StevenW.Klassen It is a part of the template (a second template parameter). Template parameters of the deduction guide don't match template parameters of the class here.
– HolyBlackCat
2 days ago
add a comment |
This is the closest I've seen to an answer, falling short only in that the array size is not part of the template.
– Steven W. Klassen
Nov 9 at 8:09
@StevenW.Klassen It is a part of the template (a second template parameter). Template parameters of the deduction guide don't match template parameters of the class here.
– HolyBlackCat
2 days ago
This is the closest I've seen to an answer, falling short only in that the array size is not part of the template.
– Steven W. Klassen
Nov 9 at 8:09
This is the closest I've seen to an answer, falling short only in that the array size is not part of the template.
– Steven W. Klassen
Nov 9 at 8:09
@StevenW.Klassen It is a part of the template (a second template parameter). Template parameters of the deduction guide don't match template parameters of the class here.
– HolyBlackCat
2 days ago
@StevenW.Klassen It is a part of the template (a second template parameter). Template parameters of the deduction guide don't match template parameters of the class here.
– HolyBlackCat
2 days ago
add a comment |
up vote
5
down vote
I hate to be the bearer of bad news, but I believe that at the current time (at least as of C++17) the answer to your question is "no" there is no way that would meet all of your given requirements. That said, there are solutions posted here that come close, but they all fall short in one or more of your requirements.
I also suspect that the answer will remain "no" in the near future. Not that I'm a prophet, but the direction of the last few C++ versions would seem to imply that a make_array
solution is more likely to be what is added rather than more direct language support.
Explanation
Allow me to explain why in a little more detail.
First consider the C++17 deduction guides. I won't go into detail on them as they are discussed suitably by other answers on this question. They do come very close to what you are asking but do seem to fall short in one way or the other. (Although the answer by @max66 would seem to meet all your requirements except for the extra brackets. If his syntax in fact works you may want to consider that answer as "close enough".)
Next consider a variatic templates solution. In order to automatically determine N you would need a series of overloaded functions (basically one with a single argument and one that takes a single argument and the rest of the variatic templates). But that would essentially be equivalent to some form of make_array
so it doesn't count either.
Finally, the only other option I could see would be based on an initializer_list. The question is how to determine N from that list. In C++11 this would be obviously impossible since the only access to the size of the list is const
and not constexpr
. However as of C++14 the size()
method is in fact constexpr
so you would think it is at least theoretically possible to get the compiler to deduce N based on that. Unfortunately to do this you would need to make N (a template parameter) default to a value of something in the class constructor (the initializer list). I have been unable to determine any means of doing this in the present form of the language.
How I think a future version could support this
One way to support this would be to follow the examples of other languages and add direct language support, bridging a syntax to certain classes. But this essentially makes some classes "special". Consider the following line in Swift:
let ar = [1, 2, 3, 4]
In this example ar
is an object of the type Array<Int>
. But this is done by direct compiler support, i.e. "Array" is a special case. No matter what you do you could not write a MyArray class that would perform the same way (other than perhaps having MyArray accept an Array as a construction option). It is certainly possible for the C++ standard to be extended to do something similar, but C++ tends to try to avoid those "special" cases. Besides, an argument could be made that
auto ar = make_array(1, 2, 3, 4);
is in fact a clearer representation of the intent than would be something like the following: (suggesting the character 'A' to distinguish this from an initializer list)
auto ar = A 1, 2, 3, 4 ;
Another way, more in line with current C++ syntax, to do this would be to add the N parameter to the initializer_list template class. After all since size()
is now constexpr
the size must be known at compile time, so why not make it available as a template parameter? It could suitably default so that it could rarely be needed, but it would allow classes (both standard ones like std::array
and custom ones like you are proposing) the possibility of tying the N in the Array
template to the N
in the initializer_list
. Then you should be able to write something along the following lines:
template<class T, size_t N>
struct Array
explicit Array(std::initializer_list<N> il);
Of course the trick would be to make this initializer_list
change in a fashion that does not break a lot of existing code.
I suspect the standards committee will not follow either of these paths, but will more likely add the experimental make_array
method. And I'm not convinced that is a bad idea. We are used to make_...
in many other parts of the language, so why not here as well?
1
My answer isn’t make_array, my answer is no there isn’t at the current time. The experimental make_array may be an indication that something is perhaps being considered.
– Steven W. Klassen
Nov 8 at 18:41
That said, there are some techniques being listed here that could yet prove me wrong. (Perhaps the answer is not ‘no’.)
– Steven W. Klassen
Nov 8 at 18:43
1
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - From Review
– Ken Y-N
Nov 8 at 23:48
1
The reviewers have a divided opinion of my post with half saying delete and half saying it is ok. Before going the delete route, I'm going to rephrase my answer and explain in more detail why I believe the correct answer is in fact (and unfortunately) "no".
– Steven W. Klassen
Nov 9 at 8:02
add a comment |
up vote
5
down vote
I hate to be the bearer of bad news, but I believe that at the current time (at least as of C++17) the answer to your question is "no" there is no way that would meet all of your given requirements. That said, there are solutions posted here that come close, but they all fall short in one or more of your requirements.
I also suspect that the answer will remain "no" in the near future. Not that I'm a prophet, but the direction of the last few C++ versions would seem to imply that a make_array
solution is more likely to be what is added rather than more direct language support.
Explanation
Allow me to explain why in a little more detail.
First consider the C++17 deduction guides. I won't go into detail on them as they are discussed suitably by other answers on this question. They do come very close to what you are asking but do seem to fall short in one way or the other. (Although the answer by @max66 would seem to meet all your requirements except for the extra brackets. If his syntax in fact works you may want to consider that answer as "close enough".)
Next consider a variatic templates solution. In order to automatically determine N you would need a series of overloaded functions (basically one with a single argument and one that takes a single argument and the rest of the variatic templates). But that would essentially be equivalent to some form of make_array
so it doesn't count either.
Finally, the only other option I could see would be based on an initializer_list. The question is how to determine N from that list. In C++11 this would be obviously impossible since the only access to the size of the list is const
and not constexpr
. However as of C++14 the size()
method is in fact constexpr
so you would think it is at least theoretically possible to get the compiler to deduce N based on that. Unfortunately to do this you would need to make N (a template parameter) default to a value of something in the class constructor (the initializer list). I have been unable to determine any means of doing this in the present form of the language.
How I think a future version could support this
One way to support this would be to follow the examples of other languages and add direct language support, bridging a syntax to certain classes. But this essentially makes some classes "special". Consider the following line in Swift:
let ar = [1, 2, 3, 4]
In this example ar
is an object of the type Array<Int>
. But this is done by direct compiler support, i.e. "Array" is a special case. No matter what you do you could not write a MyArray class that would perform the same way (other than perhaps having MyArray accept an Array as a construction option). It is certainly possible for the C++ standard to be extended to do something similar, but C++ tends to try to avoid those "special" cases. Besides, an argument could be made that
auto ar = make_array(1, 2, 3, 4);
is in fact a clearer representation of the intent than would be something like the following: (suggesting the character 'A' to distinguish this from an initializer list)
auto ar = A 1, 2, 3, 4 ;
Another way, more in line with current C++ syntax, to do this would be to add the N parameter to the initializer_list template class. After all since size()
is now constexpr
the size must be known at compile time, so why not make it available as a template parameter? It could suitably default so that it could rarely be needed, but it would allow classes (both standard ones like std::array
and custom ones like you are proposing) the possibility of tying the N in the Array
template to the N
in the initializer_list
. Then you should be able to write something along the following lines:
template<class T, size_t N>
struct Array
explicit Array(std::initializer_list<N> il);
Of course the trick would be to make this initializer_list
change in a fashion that does not break a lot of existing code.
I suspect the standards committee will not follow either of these paths, but will more likely add the experimental make_array
method. And I'm not convinced that is a bad idea. We are used to make_...
in many other parts of the language, so why not here as well?
1
My answer isn’t make_array, my answer is no there isn’t at the current time. The experimental make_array may be an indication that something is perhaps being considered.
– Steven W. Klassen
Nov 8 at 18:41
That said, there are some techniques being listed here that could yet prove me wrong. (Perhaps the answer is not ‘no’.)
– Steven W. Klassen
Nov 8 at 18:43
1
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - From Review
– Ken Y-N
Nov 8 at 23:48
1
The reviewers have a divided opinion of my post with half saying delete and half saying it is ok. Before going the delete route, I'm going to rephrase my answer and explain in more detail why I believe the correct answer is in fact (and unfortunately) "no".
– Steven W. Klassen
Nov 9 at 8:02
add a comment |
up vote
5
down vote
up vote
5
down vote
I hate to be the bearer of bad news, but I believe that at the current time (at least as of C++17) the answer to your question is "no" there is no way that would meet all of your given requirements. That said, there are solutions posted here that come close, but they all fall short in one or more of your requirements.
I also suspect that the answer will remain "no" in the near future. Not that I'm a prophet, but the direction of the last few C++ versions would seem to imply that a make_array
solution is more likely to be what is added rather than more direct language support.
Explanation
Allow me to explain why in a little more detail.
First consider the C++17 deduction guides. I won't go into detail on them as they are discussed suitably by other answers on this question. They do come very close to what you are asking but do seem to fall short in one way or the other. (Although the answer by @max66 would seem to meet all your requirements except for the extra brackets. If his syntax in fact works you may want to consider that answer as "close enough".)
Next consider a variatic templates solution. In order to automatically determine N you would need a series of overloaded functions (basically one with a single argument and one that takes a single argument and the rest of the variatic templates). But that would essentially be equivalent to some form of make_array
so it doesn't count either.
Finally, the only other option I could see would be based on an initializer_list. The question is how to determine N from that list. In C++11 this would be obviously impossible since the only access to the size of the list is const
and not constexpr
. However as of C++14 the size()
method is in fact constexpr
so you would think it is at least theoretically possible to get the compiler to deduce N based on that. Unfortunately to do this you would need to make N (a template parameter) default to a value of something in the class constructor (the initializer list). I have been unable to determine any means of doing this in the present form of the language.
How I think a future version could support this
One way to support this would be to follow the examples of other languages and add direct language support, bridging a syntax to certain classes. But this essentially makes some classes "special". Consider the following line in Swift:
let ar = [1, 2, 3, 4]
In this example ar
is an object of the type Array<Int>
. But this is done by direct compiler support, i.e. "Array" is a special case. No matter what you do you could not write a MyArray class that would perform the same way (other than perhaps having MyArray accept an Array as a construction option). It is certainly possible for the C++ standard to be extended to do something similar, but C++ tends to try to avoid those "special" cases. Besides, an argument could be made that
auto ar = make_array(1, 2, 3, 4);
is in fact a clearer representation of the intent than would be something like the following: (suggesting the character 'A' to distinguish this from an initializer list)
auto ar = A 1, 2, 3, 4 ;
Another way, more in line with current C++ syntax, to do this would be to add the N parameter to the initializer_list template class. After all since size()
is now constexpr
the size must be known at compile time, so why not make it available as a template parameter? It could suitably default so that it could rarely be needed, but it would allow classes (both standard ones like std::array
and custom ones like you are proposing) the possibility of tying the N in the Array
template to the N
in the initializer_list
. Then you should be able to write something along the following lines:
template<class T, size_t N>
struct Array
explicit Array(std::initializer_list<N> il);
Of course the trick would be to make this initializer_list
change in a fashion that does not break a lot of existing code.
I suspect the standards committee will not follow either of these paths, but will more likely add the experimental make_array
method. And I'm not convinced that is a bad idea. We are used to make_...
in many other parts of the language, so why not here as well?
I hate to be the bearer of bad news, but I believe that at the current time (at least as of C++17) the answer to your question is "no" there is no way that would meet all of your given requirements. That said, there are solutions posted here that come close, but they all fall short in one or more of your requirements.
I also suspect that the answer will remain "no" in the near future. Not that I'm a prophet, but the direction of the last few C++ versions would seem to imply that a make_array
solution is more likely to be what is added rather than more direct language support.
Explanation
Allow me to explain why in a little more detail.
First consider the C++17 deduction guides. I won't go into detail on them as they are discussed suitably by other answers on this question. They do come very close to what you are asking but do seem to fall short in one way or the other. (Although the answer by @max66 would seem to meet all your requirements except for the extra brackets. If his syntax in fact works you may want to consider that answer as "close enough".)
Next consider a variatic templates solution. In order to automatically determine N you would need a series of overloaded functions (basically one with a single argument and one that takes a single argument and the rest of the variatic templates). But that would essentially be equivalent to some form of make_array
so it doesn't count either.
Finally, the only other option I could see would be based on an initializer_list. The question is how to determine N from that list. In C++11 this would be obviously impossible since the only access to the size of the list is const
and not constexpr
. However as of C++14 the size()
method is in fact constexpr
so you would think it is at least theoretically possible to get the compiler to deduce N based on that. Unfortunately to do this you would need to make N (a template parameter) default to a value of something in the class constructor (the initializer list). I have been unable to determine any means of doing this in the present form of the language.
How I think a future version could support this
One way to support this would be to follow the examples of other languages and add direct language support, bridging a syntax to certain classes. But this essentially makes some classes "special". Consider the following line in Swift:
let ar = [1, 2, 3, 4]
In this example ar
is an object of the type Array<Int>
. But this is done by direct compiler support, i.e. "Array" is a special case. No matter what you do you could not write a MyArray class that would perform the same way (other than perhaps having MyArray accept an Array as a construction option). It is certainly possible for the C++ standard to be extended to do something similar, but C++ tends to try to avoid those "special" cases. Besides, an argument could be made that
auto ar = make_array(1, 2, 3, 4);
is in fact a clearer representation of the intent than would be something like the following: (suggesting the character 'A' to distinguish this from an initializer list)
auto ar = A 1, 2, 3, 4 ;
Another way, more in line with current C++ syntax, to do this would be to add the N parameter to the initializer_list template class. After all since size()
is now constexpr
the size must be known at compile time, so why not make it available as a template parameter? It could suitably default so that it could rarely be needed, but it would allow classes (both standard ones like std::array
and custom ones like you are proposing) the possibility of tying the N in the Array
template to the N
in the initializer_list
. Then you should be able to write something along the following lines:
template<class T, size_t N>
struct Array
explicit Array(std::initializer_list<N> il);
Of course the trick would be to make this initializer_list
change in a fashion that does not break a lot of existing code.
I suspect the standards committee will not follow either of these paths, but will more likely add the experimental make_array
method. And I'm not convinced that is a bad idea. We are used to make_...
in many other parts of the language, so why not here as well?
edited Nov 9 at 9:01
answered Nov 8 at 8:23
Steven W. Klassen
22418
22418
1
My answer isn’t make_array, my answer is no there isn’t at the current time. The experimental make_array may be an indication that something is perhaps being considered.
– Steven W. Klassen
Nov 8 at 18:41
That said, there are some techniques being listed here that could yet prove me wrong. (Perhaps the answer is not ‘no’.)
– Steven W. Klassen
Nov 8 at 18:43
1
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - From Review
– Ken Y-N
Nov 8 at 23:48
1
The reviewers have a divided opinion of my post with half saying delete and half saying it is ok. Before going the delete route, I'm going to rephrase my answer and explain in more detail why I believe the correct answer is in fact (and unfortunately) "no".
– Steven W. Klassen
Nov 9 at 8:02
add a comment |
1
My answer isn’t make_array, my answer is no there isn’t at the current time. The experimental make_array may be an indication that something is perhaps being considered.
– Steven W. Klassen
Nov 8 at 18:41
That said, there are some techniques being listed here that could yet prove me wrong. (Perhaps the answer is not ‘no’.)
– Steven W. Klassen
Nov 8 at 18:43
1
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - From Review
– Ken Y-N
Nov 8 at 23:48
1
The reviewers have a divided opinion of my post with half saying delete and half saying it is ok. Before going the delete route, I'm going to rephrase my answer and explain in more detail why I believe the correct answer is in fact (and unfortunately) "no".
– Steven W. Klassen
Nov 9 at 8:02
1
1
My answer isn’t make_array, my answer is no there isn’t at the current time. The experimental make_array may be an indication that something is perhaps being considered.
– Steven W. Klassen
Nov 8 at 18:41
My answer isn’t make_array, my answer is no there isn’t at the current time. The experimental make_array may be an indication that something is perhaps being considered.
– Steven W. Klassen
Nov 8 at 18:41
That said, there are some techniques being listed here that could yet prove me wrong. (Perhaps the answer is not ‘no’.)
– Steven W. Klassen
Nov 8 at 18:43
That said, there are some techniques being listed here that could yet prove me wrong. (Perhaps the answer is not ‘no’.)
– Steven W. Klassen
Nov 8 at 18:43
1
1
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - From Review
– Ken Y-N
Nov 8 at 23:48
This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post. - From Review
– Ken Y-N
Nov 8 at 23:48
1
1
The reviewers have a divided opinion of my post with half saying delete and half saying it is ok. Before going the delete route, I'm going to rephrase my answer and explain in more detail why I believe the correct answer is in fact (and unfortunately) "no".
– Steven W. Klassen
Nov 9 at 8:02
The reviewers have a divided opinion of my post with half saying delete and half saying it is ok. Before going the delete route, I'm going to rephrase my answer and explain in more detail why I believe the correct answer is in fact (and unfortunately) "no".
– Steven W. Klassen
Nov 9 at 8:02
add a comment |
up vote
1
down vote
Here's one approach that allows you to specify the type. We use an extra argument to specify the type, so that we can still use the deduction guide to pick up the size. I don't consider it pretty though:
#include <iostream>
template <typename T>
struct Tag ;
template <typename T, size_t N>
struct Array
T data_[N];
template <typename... U>
Array(Tag<T>, U... u)
: data_static_cast<T>(u)... // cast to shut up narrowing conversions - bad idea??
;
template <typename T, typename... U>
Array(Tag<T>, U...) -> Array<T, sizeof...(U)>;
int main()
Array aTag<double>, 1, 2.0f, 3.0;
for (auto d : a.data_)
std::cout << d << 'n';
This is clearly not a full implementation of such a class, just to illustrate the technique.
1
Nice; I suggest to add, a SFINAE enable check in template signature (something astemplate <typename ... U, typename = std::enable_if_t<N == sizeof...(U)>>
(or maybe is enoughsizeof...(U) <= N
) to avoid that someone can write something asArray<int, 2> aTag<int>, 1, 2, 3;
– max66
Nov 8 at 15:07
add a comment |
up vote
1
down vote
Here's one approach that allows you to specify the type. We use an extra argument to specify the type, so that we can still use the deduction guide to pick up the size. I don't consider it pretty though:
#include <iostream>
template <typename T>
struct Tag ;
template <typename T, size_t N>
struct Array
T data_[N];
template <typename... U>
Array(Tag<T>, U... u)
: data_static_cast<T>(u)... // cast to shut up narrowing conversions - bad idea??
;
template <typename T, typename... U>
Array(Tag<T>, U...) -> Array<T, sizeof...(U)>;
int main()
Array aTag<double>, 1, 2.0f, 3.0;
for (auto d : a.data_)
std::cout << d << 'n';
This is clearly not a full implementation of such a class, just to illustrate the technique.
1
Nice; I suggest to add, a SFINAE enable check in template signature (something astemplate <typename ... U, typename = std::enable_if_t<N == sizeof...(U)>>
(or maybe is enoughsizeof...(U) <= N
) to avoid that someone can write something asArray<int, 2> aTag<int>, 1, 2, 3;
– max66
Nov 8 at 15:07
add a comment |
up vote
1
down vote
up vote
1
down vote
Here's one approach that allows you to specify the type. We use an extra argument to specify the type, so that we can still use the deduction guide to pick up the size. I don't consider it pretty though:
#include <iostream>
template <typename T>
struct Tag ;
template <typename T, size_t N>
struct Array
T data_[N];
template <typename... U>
Array(Tag<T>, U... u)
: data_static_cast<T>(u)... // cast to shut up narrowing conversions - bad idea??
;
template <typename T, typename... U>
Array(Tag<T>, U...) -> Array<T, sizeof...(U)>;
int main()
Array aTag<double>, 1, 2.0f, 3.0;
for (auto d : a.data_)
std::cout << d << 'n';
This is clearly not a full implementation of such a class, just to illustrate the technique.
Here's one approach that allows you to specify the type. We use an extra argument to specify the type, so that we can still use the deduction guide to pick up the size. I don't consider it pretty though:
#include <iostream>
template <typename T>
struct Tag ;
template <typename T, size_t N>
struct Array
T data_[N];
template <typename... U>
Array(Tag<T>, U... u)
: data_static_cast<T>(u)... // cast to shut up narrowing conversions - bad idea??
;
template <typename T, typename... U>
Array(Tag<T>, U...) -> Array<T, sizeof...(U)>;
int main()
Array aTag<double>, 1, 2.0f, 3.0;
for (auto d : a.data_)
std::cout << d << 'n';
This is clearly not a full implementation of such a class, just to illustrate the technique.
edited Nov 8 at 15:48
answered Nov 8 at 15:03
BoBTFish
14.5k23356
14.5k23356
1
Nice; I suggest to add, a SFINAE enable check in template signature (something astemplate <typename ... U, typename = std::enable_if_t<N == sizeof...(U)>>
(or maybe is enoughsizeof...(U) <= N
) to avoid that someone can write something asArray<int, 2> aTag<int>, 1, 2, 3;
– max66
Nov 8 at 15:07
add a comment |
1
Nice; I suggest to add, a SFINAE enable check in template signature (something astemplate <typename ... U, typename = std::enable_if_t<N == sizeof...(U)>>
(or maybe is enoughsizeof...(U) <= N
) to avoid that someone can write something asArray<int, 2> aTag<int>, 1, 2, 3;
– max66
Nov 8 at 15:07
1
1
Nice; I suggest to add, a SFINAE enable check in template signature (something as
template <typename ... U, typename = std::enable_if_t<N == sizeof...(U)>>
(or maybe is enough sizeof...(U) <= N
) to avoid that someone can write something as Array<int, 2> aTag<int>, 1, 2, 3;
– max66
Nov 8 at 15:07
Nice; I suggest to add, a SFINAE enable check in template signature (something as
template <typename ... U, typename = std::enable_if_t<N == sizeof...(U)>>
(or maybe is enough sizeof...(U) <= N
) to avoid that someone can write something as Array<int, 2> aTag<int>, 1, 2, 3;
– max66
Nov 8 at 15:07
add a comment |
up vote
1
down vote
Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Not necessarily.
Yes, you can't explicit the type parameter, but you can decide it according the types of the items.
I imagine two reasonable strategies: (1) the type of the Array
is the type of the first item, following the std::array
way, so writing the following deduction guide
template <typename T, typename ... Us>
Array(T, Us...) -> Array<T, 1u + sizeof...(Us)>;
(but observe that a C++ program where a Us
type is different from T
, for a std::array
, is ill formed) or (2) follows the metalfox's suggestion and select the item's common type
template <typename ... Ts>
Array(Ts...) -> Array<std::common_type_t<Ts...>, sizeof...(Ts)>;
Is the braced-init-list to
T[N]
that happens in intfoo = 1, 2, 3 ;
purely compiler magic that can't be replicated in code?
Are you thinking in a deduction guide as follows ?
template <typename T, std::size_t N>
Array(T const (&)[N]) -> Array<T, N>;
Works but with a couple of drawbacks: (1) you have to add a couple of brackets using it
// added ---V V--- added
Array foo1 = 1, 2, 3 ; // Works
and (2) remain the problem that all items must have the same type
Array foo2 = 1.0, 2.0, 3.0f ; //Doesn't work: incompatible types
or the compiler can't deduce the type T
P.s.: what's wrong with a make_array()
function ?
P.s.2: I suggest to give a look at BoBTFish's answer to see a nice method to bypass the impossibility to explicit a template argument using deduction guides.
add a comment |
up vote
1
down vote
Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Not necessarily.
Yes, you can't explicit the type parameter, but you can decide it according the types of the items.
I imagine two reasonable strategies: (1) the type of the Array
is the type of the first item, following the std::array
way, so writing the following deduction guide
template <typename T, typename ... Us>
Array(T, Us...) -> Array<T, 1u + sizeof...(Us)>;
(but observe that a C++ program where a Us
type is different from T
, for a std::array
, is ill formed) or (2) follows the metalfox's suggestion and select the item's common type
template <typename ... Ts>
Array(Ts...) -> Array<std::common_type_t<Ts...>, sizeof...(Ts)>;
Is the braced-init-list to
T[N]
that happens in intfoo = 1, 2, 3 ;
purely compiler magic that can't be replicated in code?
Are you thinking in a deduction guide as follows ?
template <typename T, std::size_t N>
Array(T const (&)[N]) -> Array<T, N>;
Works but with a couple of drawbacks: (1) you have to add a couple of brackets using it
// added ---V V--- added
Array foo1 = 1, 2, 3 ; // Works
and (2) remain the problem that all items must have the same type
Array foo2 = 1.0, 2.0, 3.0f ; //Doesn't work: incompatible types
or the compiler can't deduce the type T
P.s.: what's wrong with a make_array()
function ?
P.s.2: I suggest to give a look at BoBTFish's answer to see a nice method to bypass the impossibility to explicit a template argument using deduction guides.
add a comment |
up vote
1
down vote
up vote
1
down vote
Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Not necessarily.
Yes, you can't explicit the type parameter, but you can decide it according the types of the items.
I imagine two reasonable strategies: (1) the type of the Array
is the type of the first item, following the std::array
way, so writing the following deduction guide
template <typename T, typename ... Us>
Array(T, Us...) -> Array<T, 1u + sizeof...(Us)>;
(but observe that a C++ program where a Us
type is different from T
, for a std::array
, is ill formed) or (2) follows the metalfox's suggestion and select the item's common type
template <typename ... Ts>
Array(Ts...) -> Array<std::common_type_t<Ts...>, sizeof...(Ts)>;
Is the braced-init-list to
T[N]
that happens in intfoo = 1, 2, 3 ;
purely compiler magic that can't be replicated in code?
Are you thinking in a deduction guide as follows ?
template <typename T, std::size_t N>
Array(T const (&)[N]) -> Array<T, N>;
Works but with a couple of drawbacks: (1) you have to add a couple of brackets using it
// added ---V V--- added
Array foo1 = 1, 2, 3 ; // Works
and (2) remain the problem that all items must have the same type
Array foo2 = 1.0, 2.0, 3.0f ; //Doesn't work: incompatible types
or the compiler can't deduce the type T
P.s.: what's wrong with a make_array()
function ?
P.s.2: I suggest to give a look at BoBTFish's answer to see a nice method to bypass the impossibility to explicit a template argument using deduction guides.
Deduction guides in C++17 nearly work, but you have to omit the type parameter and all items must have exactly the same type
Not necessarily.
Yes, you can't explicit the type parameter, but you can decide it according the types of the items.
I imagine two reasonable strategies: (1) the type of the Array
is the type of the first item, following the std::array
way, so writing the following deduction guide
template <typename T, typename ... Us>
Array(T, Us...) -> Array<T, 1u + sizeof...(Us)>;
(but observe that a C++ program where a Us
type is different from T
, for a std::array
, is ill formed) or (2) follows the metalfox's suggestion and select the item's common type
template <typename ... Ts>
Array(Ts...) -> Array<std::common_type_t<Ts...>, sizeof...(Ts)>;
Is the braced-init-list to
T[N]
that happens in intfoo = 1, 2, 3 ;
purely compiler magic that can't be replicated in code?
Are you thinking in a deduction guide as follows ?
template <typename T, std::size_t N>
Array(T const (&)[N]) -> Array<T, N>;
Works but with a couple of drawbacks: (1) you have to add a couple of brackets using it
// added ---V V--- added
Array foo1 = 1, 2, 3 ; // Works
and (2) remain the problem that all items must have the same type
Array foo2 = 1.0, 2.0, 3.0f ; //Doesn't work: incompatible types
or the compiler can't deduce the type T
P.s.: what's wrong with a make_array()
function ?
P.s.2: I suggest to give a look at BoBTFish's answer to see a nice method to bypass the impossibility to explicit a template argument using deduction guides.
edited Nov 8 at 17:00
answered Nov 8 at 13:59
max66
32k63660
32k63660
add a comment |
add a comment |
up vote
0
down vote
You had asked this:
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, and without a 'make_array' function.
I have worked on an implementation of a class that behaves in the manner you are describing only it has a little bit more complexity to it but still fairly simple, readable, reusable, portable and generic.
I was not able to have a T items
array as a direct member in the class. I had to use a T* items
instead and create an overloaded operator
in the derived class to mimic the behavior of an array. This doesn't mean that there isn't any work around to this as others have shown. I just find that this is one possible solution without having to specify the size of the array.
I use a base class to store the elements from the constructor either by means of an std::initializer_list
or of a variadic constructor
. The class templates themselves are not a variadic template only their constructor's are. The base class stores the values from the initializer_list
or parameter pack
into an std::vector
. The inherited class stores the contents from the vector
into T*
by calling the data()
function of the vector class.
template<typename T>
class ParamPack
protected:
std::vector<T> values_;
size_t size_;
public:
template<typename... U>
ParamPack( U... u ) :
values_ static_cast<T>(u)... ,
size_( sizeof...(U) )
template<typename ... U>
ParamPack( std::initializer_list<std::is_same<T, U...>( U...)> il ) :
values_( il ), size_( il.size() )
std::vector<T>& operator()() return values_;
size_t size() const return size_;
;
template<typename T>
class Array : public ParamPack<T>
private:
T* items_;
public:
template<typename... U>
Array( U... u ) : ParamPack<T>::ParamPack( u... )
items_ = this->values_.data();
template<typename... U>
Array( std::initializer_list<U...> il ) : ParamPack<T>::ParamPack( il )
items_ = this->values_.data();
T& operator( size_t idx )
return items_[idx];
T operator( size_t idx ) const
return items_[idx];
T* data() const return items_;
;
int main()
try Elements: ";
for( auto& v : pp3() )
std::cout << v << " ";
std::cout << "nn";
// Array Examples:
std::cout << "Array<T> Examples:n";
std::cout << "Using Array<T>'s initializer_list Constructorn";
Array<int> arr( il );
for( size_t i = 0; i < arr.size(); i++ )
std::cout << arr[i] << " ";
std::cout << "n";
// Using Variadic Constructor
std::cout << "Using Array<T>'s Variadic Constructorn";
Array<int> testA( 9, 8, 7, 6 );
for( size_t i = 0; i < testA.size(); i++ )
std::cout << testA[i] << " ";
std::cout << 'n';
Array<std::string> testB( "Hello", " World" );
for( size_t i = 0; i < testB.size(); i++ )
std::cout << testB[i] << " ";
std::cout << "nn";
// Using Constructor w/ Initializer List
std::cout << "Using Array<T>'s Variadic Constructor with Initializer Listn";
Array<int> testC( 105, 210, 420 );
for( size_t i = 0; i < testC.size(); i++ )
std::cout << testC[i] << " ";
std::cout << "nn";
// Using Initializer List with =
std::cout << "Using Array<T>'s Initializer List with =n";
Array<int> a = 1, 2, 3, 4 ;
for( size_t i = 0; i < a.size(); i++ )
std::cout << a[i] << " ";
std::cout << 'n';
Array<char> b = 'a', 'b', 'c', 'd' ;
for ( size_t i = 0; i < b.size(); i++ )
std::cout << b[i] << " ";
std::cout << 'n';
Array<double> c = 1.2, 3.4, 4.5, 6.7 ;
for( size_t i = 0; i < c.size(); i++ )
std::cout << c[i] << " ";
std::cout << "nn";
// Using Initializer List directly
std::cout << "Using Array<T>'s Initalizer List directlyn";
Array<uint32_t> a1 3, 6, 9, 12 ;
for( size_t i = 0; i < a1.size(); i++ )
std::cout << a1[i] << " ";
std::cout << "nn";
// Using user defined data type
struct Point
int x_, y_;
Point( int x, int y ) : x_( x ), y_( y )
;
Point p1( 1, 2 ), p2( 3, 4 ), p3( 5, 6 );
// Variadic Constructor
std::cout << "Using Array<T>'s Variadic Consturctor with user data typen";
Array<Point> d1( p1, p2, p3 );
for( size_t i = 0; i < d1.size(); i++ )
std::cout << "(" << d1[i].x_ << "," << d1[i].y_ << ") ";
std::cout << 'n';
// Initializer List Construtor (reversed order)
std::cout << "Using Array<T>'s Initializer List Constructor with user data typen";
Array<Point> d2( p3, p2, p1 );
for( size_t i = 0; i < d2.size(); i++ )
std::cout << "(" << d2[i].x_ << "," << d2[i].y_ << ") ";
std::cout << 'n';
// Initializer List Version = ... p2 first
std::cout << "Using Array<T>'s = Initializer List with user data typen";
Array<Point> d3 = p2, p1, p3 ;
for( size_t i = 0; i < d3.size(); i++ )
std::cout << "(" << d3[i].x_ << "," << d3[i].y_ << ") ";
std::cout << 'n';
// Initializer List Directly p2 first p1 & p3 swapped
std::cout << "Using Array<T>'s Initializer List directly with user data typen";
Array<Point> d4 p2, p3, p1 ;
for( size_t i = 0; i < d4.size(); i++ )
std::cout << "(" << d4[i].x_ << "," << d4[i].y_ << ") ";
std::cout << 'n';
std::initializer_list<Point> ilPoints p1, p2, p3 ;
std::cout << "Using Array<T>'s initializer_list Constructor with user data typen";
Array<Point> d5( ilPoints );
for( size_t i = 0; i < d5.size(); i++ )
std::cout << "(" << d5[i].x_ << "," << d5[i].y_ << ") ";
std::cout << "nn";
// Need a local copy of the vector instead?
std::cout << "Using Array<T>'s base class's operator()() to retrieve vectorn";
std::vector<Point> points = d4(); // using operator()()
for( auto& p : points )
std::cout << "(" << p.x_ << "," << p.y_ << ") ";
std::cout << 'n';
// Need a local copy of the pointer instead?
std::cout << "Using Array<T>'s data() to get the contents of its internal pointern";
Point* pPoint = nullptr;
pPoint = d4.data();
for( size_t i = 0; i < d4.size(); i++ )
std::cout << "(" << pPoint[i].x_ << "," << pPoint[i].y_ << ") ";
std::cout << 'n';
catch( const std::runtime_error& e )
std::cerr << e.what() << 'n';
return EXIT_FAILURE;
return EXIT_SUCCESS;
-Output-
ParamPack<T> Examples:
Using ParamPack<T>'s Variadic Constructor
Size: 4 | Elements: 1 2 3 4
Using ParamPack<T>'s Variadic Constructor with an Initializer List
Size: 4 | Elements: 5 6 7 8
Using ParamPack<T>'s initializer_list Constructor
Size: 4 | Elements: 9 10 11 12
Array<T> Examples:
Using Array<T>'s initializer_list Constructor
9 10 12 12
Using Array<T>'s Variadic Constructor
9 8 7 6
Hello World
Using Array<T>'s Constructor with Initializer List
105 210 420
Using Array<T>'s Initializer List with =
1 2 3 4
a b c d
1.2 3.4 5.6 7.8
Using Array<T>'s Initializer List directly
3 6 9 12
Using Array<T>'s Variadic Constructor with user data type
(1,2) (3,4) (5,6)
Using Array<T>'s Variadic Constructor With Initializer List of user data type
(5,6) (3,4) (1,2)
Using Array<T>'s = Initializer List with user data type
(3,4) (1,2) (5,6)
Using Array<T>'s Initializer List directly with user data type
(3,4) (5,6) (1,2)
Using Array<T>'s initializer_list Constructor with user data type
Using Array<T>'s base class's operator()() to retrieve vector
(3,4) (5,6) (1,2)
Using Array<T>'s data() to get the contents of its internal pointer
(3,4) (5,6) (1,2)
Now this is a little more robust as it has operators that are available from both the parent and child class, from the parent class you can get the stored vector
directly from its operator()()
. From the child class you can index into the child's stored pointer from operator()
and there is a function to return it's size. The template itself does not contain a size_t N
template argument since the size is stored internally in the base class and is determined by the size of its vector. With this I'm treating T* p
as if it was T p[size]
. This class is still not without some limitations.
-Valid Construction-
Array<int> a( 1, 2, 3, 4 ); // Variadic Constructor Okay
Array<int> a( 1,2,3,4 ); // Initializer List Constructor Okay
Array<int> a = 1, 2, 3, 4 ; // Initializer List Okay
Array<int> a 1,2,3,4 ; // Initializer List Okay
-Limitations-
However you have to explicitly
instantiate the template as these will not work as they all generate a compiler error.
Array a( 1,2,3,4 );
Array a( 1,2,3,4 );
Array a = 1, 2, 3, 4 ;
Array a 1,2,3,4 ;
-Note- There maybe more that can be done to make this more efficient, maybe even thread safe or exception safe, but this is just a generalization of the class design.
Please let me know what you think about this:
I appreciate your thoroughness. However, this answer boils down to 'allocate and copy elements into a vector' and it's needlessly complex even for that case. The defining features of an array are no dynamic allocation and a compile time known size.
– Adam
2 days ago
@Adam I know, but the problem with trying to not specific the array's size by inferring it from the amount of the parameters passed into the initializer list's constructor will not work because the size of the array has to be known at compile time. Even though you are calling the constructor on the class, the compiler has to set aside memory for the class and all of its members before it can call its ctor and thus it can not create the array without knowing how many elements of typeT
to reserve for the class's construction.
– Francis Cugler
yesterday
@Adam also your question is a very good question. I posted this answer because I enjoyed the attempt at trying to mimic a C arrays behavior. There are no direct issues with the code above, maybe some room for efficiency, but in the output section I'm kind of wording some of it wrong. Non of the examples are using the 2nd constructor of the two classes. They are all using the 1st constructor which is the variadic version. I'm working on it now to update the main and it's output to make it more clear.
– Francis Cugler
yesterday
add a comment |
up vote
0
down vote
You had asked this:
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, and without a 'make_array' function.
I have worked on an implementation of a class that behaves in the manner you are describing only it has a little bit more complexity to it but still fairly simple, readable, reusable, portable and generic.
I was not able to have a T items
array as a direct member in the class. I had to use a T* items
instead and create an overloaded operator
in the derived class to mimic the behavior of an array. This doesn't mean that there isn't any work around to this as others have shown. I just find that this is one possible solution without having to specify the size of the array.
I use a base class to store the elements from the constructor either by means of an std::initializer_list
or of a variadic constructor
. The class templates themselves are not a variadic template only their constructor's are. The base class stores the values from the initializer_list
or parameter pack
into an std::vector
. The inherited class stores the contents from the vector
into T*
by calling the data()
function of the vector class.
template<typename T>
class ParamPack
protected:
std::vector<T> values_;
size_t size_;
public:
template<typename... U>
ParamPack( U... u ) :
values_ static_cast<T>(u)... ,
size_( sizeof...(U) )
template<typename ... U>
ParamPack( std::initializer_list<std::is_same<T, U...>( U...)> il ) :
values_( il ), size_( il.size() )
std::vector<T>& operator()() return values_;
size_t size() const return size_;
;
template<typename T>
class Array : public ParamPack<T>
private:
T* items_;
public:
template<typename... U>
Array( U... u ) : ParamPack<T>::ParamPack( u... )
items_ = this->values_.data();
template<typename... U>
Array( std::initializer_list<U...> il ) : ParamPack<T>::ParamPack( il )
items_ = this->values_.data();
T& operator( size_t idx )
return items_[idx];
T operator( size_t idx ) const
return items_[idx];
T* data() const return items_;
;
int main()
try Elements: ";
for( auto& v : pp3() )
std::cout << v << " ";
std::cout << "nn";
// Array Examples:
std::cout << "Array<T> Examples:n";
std::cout << "Using Array<T>'s initializer_list Constructorn";
Array<int> arr( il );
for( size_t i = 0; i < arr.size(); i++ )
std::cout << arr[i] << " ";
std::cout << "n";
// Using Variadic Constructor
std::cout << "Using Array<T>'s Variadic Constructorn";
Array<int> testA( 9, 8, 7, 6 );
for( size_t i = 0; i < testA.size(); i++ )
std::cout << testA[i] << " ";
std::cout << 'n';
Array<std::string> testB( "Hello", " World" );
for( size_t i = 0; i < testB.size(); i++ )
std::cout << testB[i] << " ";
std::cout << "nn";
// Using Constructor w/ Initializer List
std::cout << "Using Array<T>'s Variadic Constructor with Initializer Listn";
Array<int> testC( 105, 210, 420 );
for( size_t i = 0; i < testC.size(); i++ )
std::cout << testC[i] << " ";
std::cout << "nn";
// Using Initializer List with =
std::cout << "Using Array<T>'s Initializer List with =n";
Array<int> a = 1, 2, 3, 4 ;
for( size_t i = 0; i < a.size(); i++ )
std::cout << a[i] << " ";
std::cout << 'n';
Array<char> b = 'a', 'b', 'c', 'd' ;
for ( size_t i = 0; i < b.size(); i++ )
std::cout << b[i] << " ";
std::cout << 'n';
Array<double> c = 1.2, 3.4, 4.5, 6.7 ;
for( size_t i = 0; i < c.size(); i++ )
std::cout << c[i] << " ";
std::cout << "nn";
// Using Initializer List directly
std::cout << "Using Array<T>'s Initalizer List directlyn";
Array<uint32_t> a1 3, 6, 9, 12 ;
for( size_t i = 0; i < a1.size(); i++ )
std::cout << a1[i] << " ";
std::cout << "nn";
// Using user defined data type
struct Point
int x_, y_;
Point( int x, int y ) : x_( x ), y_( y )
;
Point p1( 1, 2 ), p2( 3, 4 ), p3( 5, 6 );
// Variadic Constructor
std::cout << "Using Array<T>'s Variadic Consturctor with user data typen";
Array<Point> d1( p1, p2, p3 );
for( size_t i = 0; i < d1.size(); i++ )
std::cout << "(" << d1[i].x_ << "," << d1[i].y_ << ") ";
std::cout << 'n';
// Initializer List Construtor (reversed order)
std::cout << "Using Array<T>'s Initializer List Constructor with user data typen";
Array<Point> d2( p3, p2, p1 );
for( size_t i = 0; i < d2.size(); i++ )
std::cout << "(" << d2[i].x_ << "," << d2[i].y_ << ") ";
std::cout << 'n';
// Initializer List Version = ... p2 first
std::cout << "Using Array<T>'s = Initializer List with user data typen";
Array<Point> d3 = p2, p1, p3 ;
for( size_t i = 0; i < d3.size(); i++ )
std::cout << "(" << d3[i].x_ << "," << d3[i].y_ << ") ";
std::cout << 'n';
// Initializer List Directly p2 first p1 & p3 swapped
std::cout << "Using Array<T>'s Initializer List directly with user data typen";
Array<Point> d4 p2, p3, p1 ;
for( size_t i = 0; i < d4.size(); i++ )
std::cout << "(" << d4[i].x_ << "," << d4[i].y_ << ") ";
std::cout << 'n';
std::initializer_list<Point> ilPoints p1, p2, p3 ;
std::cout << "Using Array<T>'s initializer_list Constructor with user data typen";
Array<Point> d5( ilPoints );
for( size_t i = 0; i < d5.size(); i++ )
std::cout << "(" << d5[i].x_ << "," << d5[i].y_ << ") ";
std::cout << "nn";
// Need a local copy of the vector instead?
std::cout << "Using Array<T>'s base class's operator()() to retrieve vectorn";
std::vector<Point> points = d4(); // using operator()()
for( auto& p : points )
std::cout << "(" << p.x_ << "," << p.y_ << ") ";
std::cout << 'n';
// Need a local copy of the pointer instead?
std::cout << "Using Array<T>'s data() to get the contents of its internal pointern";
Point* pPoint = nullptr;
pPoint = d4.data();
for( size_t i = 0; i < d4.size(); i++ )
std::cout << "(" << pPoint[i].x_ << "," << pPoint[i].y_ << ") ";
std::cout << 'n';
catch( const std::runtime_error& e )
std::cerr << e.what() << 'n';
return EXIT_FAILURE;
return EXIT_SUCCESS;
-Output-
ParamPack<T> Examples:
Using ParamPack<T>'s Variadic Constructor
Size: 4 | Elements: 1 2 3 4
Using ParamPack<T>'s Variadic Constructor with an Initializer List
Size: 4 | Elements: 5 6 7 8
Using ParamPack<T>'s initializer_list Constructor
Size: 4 | Elements: 9 10 11 12
Array<T> Examples:
Using Array<T>'s initializer_list Constructor
9 10 12 12
Using Array<T>'s Variadic Constructor
9 8 7 6
Hello World
Using Array<T>'s Constructor with Initializer List
105 210 420
Using Array<T>'s Initializer List with =
1 2 3 4
a b c d
1.2 3.4 5.6 7.8
Using Array<T>'s Initializer List directly
3 6 9 12
Using Array<T>'s Variadic Constructor with user data type
(1,2) (3,4) (5,6)
Using Array<T>'s Variadic Constructor With Initializer List of user data type
(5,6) (3,4) (1,2)
Using Array<T>'s = Initializer List with user data type
(3,4) (1,2) (5,6)
Using Array<T>'s Initializer List directly with user data type
(3,4) (5,6) (1,2)
Using Array<T>'s initializer_list Constructor with user data type
Using Array<T>'s base class's operator()() to retrieve vector
(3,4) (5,6) (1,2)
Using Array<T>'s data() to get the contents of its internal pointer
(3,4) (5,6) (1,2)
Now this is a little more robust as it has operators that are available from both the parent and child class, from the parent class you can get the stored vector
directly from its operator()()
. From the child class you can index into the child's stored pointer from operator()
and there is a function to return it's size. The template itself does not contain a size_t N
template argument since the size is stored internally in the base class and is determined by the size of its vector. With this I'm treating T* p
as if it was T p[size]
. This class is still not without some limitations.
-Valid Construction-
Array<int> a( 1, 2, 3, 4 ); // Variadic Constructor Okay
Array<int> a( 1,2,3,4 ); // Initializer List Constructor Okay
Array<int> a = 1, 2, 3, 4 ; // Initializer List Okay
Array<int> a 1,2,3,4 ; // Initializer List Okay
-Limitations-
However you have to explicitly
instantiate the template as these will not work as they all generate a compiler error.
Array a( 1,2,3,4 );
Array a( 1,2,3,4 );
Array a = 1, 2, 3, 4 ;
Array a 1,2,3,4 ;
-Note- There maybe more that can be done to make this more efficient, maybe even thread safe or exception safe, but this is just a generalization of the class design.
Please let me know what you think about this:
I appreciate your thoroughness. However, this answer boils down to 'allocate and copy elements into a vector' and it's needlessly complex even for that case. The defining features of an array are no dynamic allocation and a compile time known size.
– Adam
2 days ago
@Adam I know, but the problem with trying to not specific the array's size by inferring it from the amount of the parameters passed into the initializer list's constructor will not work because the size of the array has to be known at compile time. Even though you are calling the constructor on the class, the compiler has to set aside memory for the class and all of its members before it can call its ctor and thus it can not create the array without knowing how many elements of typeT
to reserve for the class's construction.
– Francis Cugler
yesterday
@Adam also your question is a very good question. I posted this answer because I enjoyed the attempt at trying to mimic a C arrays behavior. There are no direct issues with the code above, maybe some room for efficiency, but in the output section I'm kind of wording some of it wrong. Non of the examples are using the 2nd constructor of the two classes. They are all using the 1st constructor which is the variadic version. I'm working on it now to update the main and it's output to make it more clear.
– Francis Cugler
yesterday
add a comment |
up vote
0
down vote
up vote
0
down vote
You had asked this:
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, and without a 'make_array' function.
I have worked on an implementation of a class that behaves in the manner you are describing only it has a little bit more complexity to it but still fairly simple, readable, reusable, portable and generic.
I was not able to have a T items
array as a direct member in the class. I had to use a T* items
instead and create an overloaded operator
in the derived class to mimic the behavior of an array. This doesn't mean that there isn't any work around to this as others have shown. I just find that this is one possible solution without having to specify the size of the array.
I use a base class to store the elements from the constructor either by means of an std::initializer_list
or of a variadic constructor
. The class templates themselves are not a variadic template only their constructor's are. The base class stores the values from the initializer_list
or parameter pack
into an std::vector
. The inherited class stores the contents from the vector
into T*
by calling the data()
function of the vector class.
template<typename T>
class ParamPack
protected:
std::vector<T> values_;
size_t size_;
public:
template<typename... U>
ParamPack( U... u ) :
values_ static_cast<T>(u)... ,
size_( sizeof...(U) )
template<typename ... U>
ParamPack( std::initializer_list<std::is_same<T, U...>( U...)> il ) :
values_( il ), size_( il.size() )
std::vector<T>& operator()() return values_;
size_t size() const return size_;
;
template<typename T>
class Array : public ParamPack<T>
private:
T* items_;
public:
template<typename... U>
Array( U... u ) : ParamPack<T>::ParamPack( u... )
items_ = this->values_.data();
template<typename... U>
Array( std::initializer_list<U...> il ) : ParamPack<T>::ParamPack( il )
items_ = this->values_.data();
T& operator( size_t idx )
return items_[idx];
T operator( size_t idx ) const
return items_[idx];
T* data() const return items_;
;
int main()
try Elements: ";
for( auto& v : pp3() )
std::cout << v << " ";
std::cout << "nn";
// Array Examples:
std::cout << "Array<T> Examples:n";
std::cout << "Using Array<T>'s initializer_list Constructorn";
Array<int> arr( il );
for( size_t i = 0; i < arr.size(); i++ )
std::cout << arr[i] << " ";
std::cout << "n";
// Using Variadic Constructor
std::cout << "Using Array<T>'s Variadic Constructorn";
Array<int> testA( 9, 8, 7, 6 );
for( size_t i = 0; i < testA.size(); i++ )
std::cout << testA[i] << " ";
std::cout << 'n';
Array<std::string> testB( "Hello", " World" );
for( size_t i = 0; i < testB.size(); i++ )
std::cout << testB[i] << " ";
std::cout << "nn";
// Using Constructor w/ Initializer List
std::cout << "Using Array<T>'s Variadic Constructor with Initializer Listn";
Array<int> testC( 105, 210, 420 );
for( size_t i = 0; i < testC.size(); i++ )
std::cout << testC[i] << " ";
std::cout << "nn";
// Using Initializer List with =
std::cout << "Using Array<T>'s Initializer List with =n";
Array<int> a = 1, 2, 3, 4 ;
for( size_t i = 0; i < a.size(); i++ )
std::cout << a[i] << " ";
std::cout << 'n';
Array<char> b = 'a', 'b', 'c', 'd' ;
for ( size_t i = 0; i < b.size(); i++ )
std::cout << b[i] << " ";
std::cout << 'n';
Array<double> c = 1.2, 3.4, 4.5, 6.7 ;
for( size_t i = 0; i < c.size(); i++ )
std::cout << c[i] << " ";
std::cout << "nn";
// Using Initializer List directly
std::cout << "Using Array<T>'s Initalizer List directlyn";
Array<uint32_t> a1 3, 6, 9, 12 ;
for( size_t i = 0; i < a1.size(); i++ )
std::cout << a1[i] << " ";
std::cout << "nn";
// Using user defined data type
struct Point
int x_, y_;
Point( int x, int y ) : x_( x ), y_( y )
;
Point p1( 1, 2 ), p2( 3, 4 ), p3( 5, 6 );
// Variadic Constructor
std::cout << "Using Array<T>'s Variadic Consturctor with user data typen";
Array<Point> d1( p1, p2, p3 );
for( size_t i = 0; i < d1.size(); i++ )
std::cout << "(" << d1[i].x_ << "," << d1[i].y_ << ") ";
std::cout << 'n';
// Initializer List Construtor (reversed order)
std::cout << "Using Array<T>'s Initializer List Constructor with user data typen";
Array<Point> d2( p3, p2, p1 );
for( size_t i = 0; i < d2.size(); i++ )
std::cout << "(" << d2[i].x_ << "," << d2[i].y_ << ") ";
std::cout << 'n';
// Initializer List Version = ... p2 first
std::cout << "Using Array<T>'s = Initializer List with user data typen";
Array<Point> d3 = p2, p1, p3 ;
for( size_t i = 0; i < d3.size(); i++ )
std::cout << "(" << d3[i].x_ << "," << d3[i].y_ << ") ";
std::cout << 'n';
// Initializer List Directly p2 first p1 & p3 swapped
std::cout << "Using Array<T>'s Initializer List directly with user data typen";
Array<Point> d4 p2, p3, p1 ;
for( size_t i = 0; i < d4.size(); i++ )
std::cout << "(" << d4[i].x_ << "," << d4[i].y_ << ") ";
std::cout << 'n';
std::initializer_list<Point> ilPoints p1, p2, p3 ;
std::cout << "Using Array<T>'s initializer_list Constructor with user data typen";
Array<Point> d5( ilPoints );
for( size_t i = 0; i < d5.size(); i++ )
std::cout << "(" << d5[i].x_ << "," << d5[i].y_ << ") ";
std::cout << "nn";
// Need a local copy of the vector instead?
std::cout << "Using Array<T>'s base class's operator()() to retrieve vectorn";
std::vector<Point> points = d4(); // using operator()()
for( auto& p : points )
std::cout << "(" << p.x_ << "," << p.y_ << ") ";
std::cout << 'n';
// Need a local copy of the pointer instead?
std::cout << "Using Array<T>'s data() to get the contents of its internal pointern";
Point* pPoint = nullptr;
pPoint = d4.data();
for( size_t i = 0; i < d4.size(); i++ )
std::cout << "(" << pPoint[i].x_ << "," << pPoint[i].y_ << ") ";
std::cout << 'n';
catch( const std::runtime_error& e )
std::cerr << e.what() << 'n';
return EXIT_FAILURE;
return EXIT_SUCCESS;
-Output-
ParamPack<T> Examples:
Using ParamPack<T>'s Variadic Constructor
Size: 4 | Elements: 1 2 3 4
Using ParamPack<T>'s Variadic Constructor with an Initializer List
Size: 4 | Elements: 5 6 7 8
Using ParamPack<T>'s initializer_list Constructor
Size: 4 | Elements: 9 10 11 12
Array<T> Examples:
Using Array<T>'s initializer_list Constructor
9 10 12 12
Using Array<T>'s Variadic Constructor
9 8 7 6
Hello World
Using Array<T>'s Constructor with Initializer List
105 210 420
Using Array<T>'s Initializer List with =
1 2 3 4
a b c d
1.2 3.4 5.6 7.8
Using Array<T>'s Initializer List directly
3 6 9 12
Using Array<T>'s Variadic Constructor with user data type
(1,2) (3,4) (5,6)
Using Array<T>'s Variadic Constructor With Initializer List of user data type
(5,6) (3,4) (1,2)
Using Array<T>'s = Initializer List with user data type
(3,4) (1,2) (5,6)
Using Array<T>'s Initializer List directly with user data type
(3,4) (5,6) (1,2)
Using Array<T>'s initializer_list Constructor with user data type
Using Array<T>'s base class's operator()() to retrieve vector
(3,4) (5,6) (1,2)
Using Array<T>'s data() to get the contents of its internal pointer
(3,4) (5,6) (1,2)
Now this is a little more robust as it has operators that are available from both the parent and child class, from the parent class you can get the stored vector
directly from its operator()()
. From the child class you can index into the child's stored pointer from operator()
and there is a function to return it's size. The template itself does not contain a size_t N
template argument since the size is stored internally in the base class and is determined by the size of its vector. With this I'm treating T* p
as if it was T p[size]
. This class is still not without some limitations.
-Valid Construction-
Array<int> a( 1, 2, 3, 4 ); // Variadic Constructor Okay
Array<int> a( 1,2,3,4 ); // Initializer List Constructor Okay
Array<int> a = 1, 2, 3, 4 ; // Initializer List Okay
Array<int> a 1,2,3,4 ; // Initializer List Okay
-Limitations-
However you have to explicitly
instantiate the template as these will not work as they all generate a compiler error.
Array a( 1,2,3,4 );
Array a( 1,2,3,4 );
Array a = 1, 2, 3, 4 ;
Array a 1,2,3,4 ;
-Note- There maybe more that can be done to make this more efficient, maybe even thread safe or exception safe, but this is just a generalization of the class design.
Please let me know what you think about this:
You had asked this:
Q: Is there anyway to create an Array class that can be initialized with a braced-init-list without having to manually specify the array length, and without a 'make_array' function.
I have worked on an implementation of a class that behaves in the manner you are describing only it has a little bit more complexity to it but still fairly simple, readable, reusable, portable and generic.
I was not able to have a T items
array as a direct member in the class. I had to use a T* items
instead and create an overloaded operator
in the derived class to mimic the behavior of an array. This doesn't mean that there isn't any work around to this as others have shown. I just find that this is one possible solution without having to specify the size of the array.
I use a base class to store the elements from the constructor either by means of an std::initializer_list
or of a variadic constructor
. The class templates themselves are not a variadic template only their constructor's are. The base class stores the values from the initializer_list
or parameter pack
into an std::vector
. The inherited class stores the contents from the vector
into T*
by calling the data()
function of the vector class.
template<typename T>
class ParamPack
protected:
std::vector<T> values_;
size_t size_;
public:
template<typename... U>
ParamPack( U... u ) :
values_ static_cast<T>(u)... ,
size_( sizeof...(U) )
template<typename ... U>
ParamPack( std::initializer_list<std::is_same<T, U...>( U...)> il ) :
values_( il ), size_( il.size() )
std::vector<T>& operator()() return values_;
size_t size() const return size_;
;
template<typename T>
class Array : public ParamPack<T>
private:
T* items_;
public:
template<typename... U>
Array( U... u ) : ParamPack<T>::ParamPack( u... )
items_ = this->values_.data();
template<typename... U>
Array( std::initializer_list<U...> il ) : ParamPack<T>::ParamPack( il )
items_ = this->values_.data();
T& operator( size_t idx )
return items_[idx];
T operator( size_t idx ) const
return items_[idx];
T* data() const return items_;
;
int main()
try Elements: ";
for( auto& v : pp3() )
std::cout << v << " ";
std::cout << "nn";
// Array Examples:
std::cout << "Array<T> Examples:n";
std::cout << "Using Array<T>'s initializer_list Constructorn";
Array<int> arr( il );
for( size_t i = 0; i < arr.size(); i++ )
std::cout << arr[i] << " ";
std::cout << "n";
// Using Variadic Constructor
std::cout << "Using Array<T>'s Variadic Constructorn";
Array<int> testA( 9, 8, 7, 6 );
for( size_t i = 0; i < testA.size(); i++ )
std::cout << testA[i] << " ";
std::cout << 'n';
Array<std::string> testB( "Hello", " World" );
for( size_t i = 0; i < testB.size(); i++ )
std::cout << testB[i] << " ";
std::cout << "nn";
// Using Constructor w/ Initializer List
std::cout << "Using Array<T>'s Variadic Constructor with Initializer Listn";
Array<int> testC( 105, 210, 420 );
for( size_t i = 0; i < testC.size(); i++ )
std::cout << testC[i] << " ";
std::cout << "nn";
// Using Initializer List with =
std::cout << "Using Array<T>'s Initializer List with =n";
Array<int> a = 1, 2, 3, 4 ;
for( size_t i = 0; i < a.size(); i++ )
std::cout << a[i] << " ";
std::cout << 'n';
Array<char> b = 'a', 'b', 'c', 'd' ;
for ( size_t i = 0; i < b.size(); i++ )
std::cout << b[i] << " ";
std::cout << 'n';
Array<double> c = 1.2, 3.4, 4.5, 6.7 ;
for( size_t i = 0; i < c.size(); i++ )
std::cout << c[i] << " ";
std::cout << "nn";
// Using Initializer List directly
std::cout << "Using Array<T>'s Initalizer List directlyn";
Array<uint32_t> a1 3, 6, 9, 12 ;
for( size_t i = 0; i < a1.size(); i++ )
std::cout << a1[i] << " ";
std::cout << "nn";
// Using user defined data type
struct Point
int x_, y_;
Point( int x, int y ) : x_( x ), y_( y )
;
Point p1( 1, 2 ), p2( 3, 4 ), p3( 5, 6 );
// Variadic Constructor
std::cout << "Using Array<T>'s Variadic Consturctor with user data typen";
Array<Point> d1( p1, p2, p3 );
for( size_t i = 0; i < d1.size(); i++ )
std::cout << "(" << d1[i].x_ << "," << d1[i].y_ << ") ";
std::cout << 'n';
// Initializer List Construtor (reversed order)
std::cout << "Using Array<T>'s Initializer List Constructor with user data typen";
Array<Point> d2( p3, p2, p1 );
for( size_t i = 0; i < d2.size(); i++ )
std::cout << "(" << d2[i].x_ << "," << d2[i].y_ << ") ";
std::cout << 'n';
// Initializer List Version = ... p2 first
std::cout << "Using Array<T>'s = Initializer List with user data typen";
Array<Point> d3 = p2, p1, p3 ;
for( size_t i = 0; i < d3.size(); i++ )
std::cout << "(" << d3[i].x_ << "," << d3[i].y_ << ") ";
std::cout << 'n';
// Initializer List Directly p2 first p1 & p3 swapped
std::cout << "Using Array<T>'s Initializer List directly with user data typen";
Array<Point> d4 p2, p3, p1 ;
for( size_t i = 0; i < d4.size(); i++ )
std::cout << "(" << d4[i].x_ << "," << d4[i].y_ << ") ";
std::cout << 'n';
std::initializer_list<Point> ilPoints p1, p2, p3 ;
std::cout << "Using Array<T>'s initializer_list Constructor with user data typen";
Array<Point> d5( ilPoints );
for( size_t i = 0; i < d5.size(); i++ )
std::cout << "(" << d5[i].x_ << "," << d5[i].y_ << ") ";
std::cout << "nn";
// Need a local copy of the vector instead?
std::cout << "Using Array<T>'s base class's operator()() to retrieve vectorn";
std::vector<Point> points = d4(); // using operator()()
for( auto& p : points )
std::cout << "(" << p.x_ << "," << p.y_ << ") ";
std::cout << 'n';
// Need a local copy of the pointer instead?
std::cout << "Using Array<T>'s data() to get the contents of its internal pointern";
Point* pPoint = nullptr;
pPoint = d4.data();
for( size_t i = 0; i < d4.size(); i++ )
std::cout << "(" << pPoint[i].x_ << "," << pPoint[i].y_ << ") ";
std::cout << 'n';
catch( const std::runtime_error& e )
std::cerr << e.what() << 'n';
return EXIT_FAILURE;
return EXIT_SUCCESS;
-Output-
ParamPack<T> Examples:
Using ParamPack<T>'s Variadic Constructor
Size: 4 | Elements: 1 2 3 4
Using ParamPack<T>'s Variadic Constructor with an Initializer List
Size: 4 | Elements: 5 6 7 8
Using ParamPack<T>'s initializer_list Constructor
Size: 4 | Elements: 9 10 11 12
Array<T> Examples:
Using Array<T>'s initializer_list Constructor
9 10 12 12
Using Array<T>'s Variadic Constructor
9 8 7 6
Hello World
Using Array<T>'s Constructor with Initializer List
105 210 420
Using Array<T>'s Initializer List with =
1 2 3 4
a b c d
1.2 3.4 5.6 7.8
Using Array<T>'s Initializer List directly
3 6 9 12
Using Array<T>'s Variadic Constructor with user data type
(1,2) (3,4) (5,6)
Using Array<T>'s Variadic Constructor With Initializer List of user data type
(5,6) (3,4) (1,2)
Using Array<T>'s = Initializer List with user data type
(3,4) (1,2) (5,6)
Using Array<T>'s Initializer List directly with user data type
(3,4) (5,6) (1,2)
Using Array<T>'s initializer_list Constructor with user data type
Using Array<T>'s base class's operator()() to retrieve vector
(3,4) (5,6) (1,2)
Using Array<T>'s data() to get the contents of its internal pointer
(3,4) (5,6) (1,2)
Now this is a little more robust as it has operators that are available from both the parent and child class, from the parent class you can get the stored vector
directly from its operator()()
. From the child class you can index into the child's stored pointer from operator()
and there is a function to return it's size. The template itself does not contain a size_t N
template argument since the size is stored internally in the base class and is determined by the size of its vector. With this I'm treating T* p
as if it was T p[size]
. This class is still not without some limitations.
-Valid Construction-
Array<int> a( 1, 2, 3, 4 ); // Variadic Constructor Okay
Array<int> a( 1,2,3,4 ); // Initializer List Constructor Okay
Array<int> a = 1, 2, 3, 4 ; // Initializer List Okay
Array<int> a 1,2,3,4 ; // Initializer List Okay
-Limitations-
However you have to explicitly
instantiate the template as these will not work as they all generate a compiler error.
Array a( 1,2,3,4 );
Array a( 1,2,3,4 );
Array a = 1, 2, 3, 4 ;
Array a 1,2,3,4 ;
-Note- There maybe more that can be done to make this more efficient, maybe even thread safe or exception safe, but this is just a generalization of the class design.
Please let me know what you think about this:
edited yesterday
answered 2 days ago
Francis Cugler
4,00711227
4,00711227
I appreciate your thoroughness. However, this answer boils down to 'allocate and copy elements into a vector' and it's needlessly complex even for that case. The defining features of an array are no dynamic allocation and a compile time known size.
– Adam
2 days ago
@Adam I know, but the problem with trying to not specific the array's size by inferring it from the amount of the parameters passed into the initializer list's constructor will not work because the size of the array has to be known at compile time. Even though you are calling the constructor on the class, the compiler has to set aside memory for the class and all of its members before it can call its ctor and thus it can not create the array without knowing how many elements of typeT
to reserve for the class's construction.
– Francis Cugler
yesterday
@Adam also your question is a very good question. I posted this answer because I enjoyed the attempt at trying to mimic a C arrays behavior. There are no direct issues with the code above, maybe some room for efficiency, but in the output section I'm kind of wording some of it wrong. Non of the examples are using the 2nd constructor of the two classes. They are all using the 1st constructor which is the variadic version. I'm working on it now to update the main and it's output to make it more clear.
– Francis Cugler
yesterday
add a comment |
I appreciate your thoroughness. However, this answer boils down to 'allocate and copy elements into a vector' and it's needlessly complex even for that case. The defining features of an array are no dynamic allocation and a compile time known size.
– Adam
2 days ago
@Adam I know, but the problem with trying to not specific the array's size by inferring it from the amount of the parameters passed into the initializer list's constructor will not work because the size of the array has to be known at compile time. Even though you are calling the constructor on the class, the compiler has to set aside memory for the class and all of its members before it can call its ctor and thus it can not create the array without knowing how many elements of typeT
to reserve for the class's construction.
– Francis Cugler
yesterday
@Adam also your question is a very good question. I posted this answer because I enjoyed the attempt at trying to mimic a C arrays behavior. There are no direct issues with the code above, maybe some room for efficiency, but in the output section I'm kind of wording some of it wrong. Non of the examples are using the 2nd constructor of the two classes. They are all using the 1st constructor which is the variadic version. I'm working on it now to update the main and it's output to make it more clear.
– Francis Cugler
yesterday
I appreciate your thoroughness. However, this answer boils down to 'allocate and copy elements into a vector' and it's needlessly complex even for that case. The defining features of an array are no dynamic allocation and a compile time known size.
– Adam
2 days ago
I appreciate your thoroughness. However, this answer boils down to 'allocate and copy elements into a vector' and it's needlessly complex even for that case. The defining features of an array are no dynamic allocation and a compile time known size.
– Adam
2 days ago
@Adam I know, but the problem with trying to not specific the array's size by inferring it from the amount of the parameters passed into the initializer list's constructor will not work because the size of the array has to be known at compile time. Even though you are calling the constructor on the class, the compiler has to set aside memory for the class and all of its members before it can call its ctor and thus it can not create the array without knowing how many elements of type
T
to reserve for the class's construction.– Francis Cugler
yesterday
@Adam I know, but the problem with trying to not specific the array's size by inferring it from the amount of the parameters passed into the initializer list's constructor will not work because the size of the array has to be known at compile time. Even though you are calling the constructor on the class, the compiler has to set aside memory for the class and all of its members before it can call its ctor and thus it can not create the array without knowing how many elements of type
T
to reserve for the class's construction.– Francis Cugler
yesterday
@Adam also your question is a very good question. I posted this answer because I enjoyed the attempt at trying to mimic a C arrays behavior. There are no direct issues with the code above, maybe some room for efficiency, but in the output section I'm kind of wording some of it wrong. Non of the examples are using the 2nd constructor of the two classes. They are all using the 1st constructor which is the variadic version. I'm working on it now to update the main and it's output to make it more clear.
– Francis Cugler
yesterday
@Adam also your question is a very good question. I posted this answer because I enjoyed the attempt at trying to mimic a C arrays behavior. There are no direct issues with the code above, maybe some room for efficiency, but in the output section I'm kind of wording some of it wrong. Non of the examples are using the 2nd constructor of the two classes. They are all using the 1st constructor which is the variadic version. I'm working on it now to update the main and it's output to make it more clear.
– Francis Cugler
yesterday
add a comment |
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53203629%2farray-class-that-will-accept-an-braced-init-list-and-deduce-length%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Maybe you could create something much like
std::array
, but force the deduction guide by using some tag type as the first constructor argument. Would look something likeArray foo = Tag<int>, 1, 2, 3;
. (I haven't thought this all the way through yet...)– BoBTFish
Nov 8 at 8:21
1
@BoBTFish - interesting idea: should permit to explicit the type, bypassing the problem that you can't explicit a template parameter using template guides; IMHO, you should elaborate it and propose it as an answer.
– max66
Nov 8 at 14:12
So, making
std::array<int>
work was proposed yesterday at the standards meeting and it got shot down, and for good reasons:std::array<int>
looks like a type and is today. Making it a placeholder would 1) potentially break existing code and 2) you would almost never know whether the type you write is also the type you get.– Rakete1111
2 days ago
I don't know what 'making it a placeholder' means. I also don't know where type ambiguity makes it into the equation. It boils down to: it should be possible to write a constructor that takes a braced-init-list and has a compile-time known size. I'm not looking to change std::array.
– Adam
2 days ago