Nested initializer_list for initializing multidimensional arrays











up vote
5
down vote

favorite












For some reasons I have to implement a multidimensional array class in C++.
The array in question is something like this:



template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
class Array final
{
private:
std::vector<size_t> shape_;
std::vector<T> data_;
public:
// Some public methods
}


T is the type of elements stored in the array, and that the dimensions of the array is not templated since the user should be able to reshape the array, for example:



Array<int> array = Array<int>::zeros(3, 2, 4);
array.reshape(4, 6);


Though implementation of the functions mentioned above went quite smoothly, I stuck at the "beginning" of implementing this class, that is to initialize the array...
My questions are as follows:





  1. Is there any method to have an constructor like this, such that nested initializer lists of different depths create different arrays, like:



    Array<int> a = {1, 2}; // Dimension: 1, Shape: 2
    Array<int> b = {{1}, {2}}; // Dimension: 2, Shape: 2x1


    My approach to implement the constructors made these two arrays the same, which is not what I want. Plus, clang complained about the braced scalars, which seem to be the problem. Currently my naive approach which suffers from the problem above looks like this



    ...
    Array() :data_(0), shape_{0} {}
    Array(std::initializer_list<T> list) :data_(list), shape_{list.size()} {}
    Array(std::initializer_list<Array> lists)
    {
    // Implementation
    }
    ...



  2. It's easy for the compiler to deduct the types for the following arrays:



    Array c = {1, 2}; // T = int
    Array d = {1.0, 2.0}; // T = double


    But I failed to write a working deduction guide for multidimensional ones:



    Array e = {{1, 2}, {3, 4}}; // Expects T = int
    Array f = {{1.0, 2.0}, {3.0, 4.0}}; // Expects T = double


    Is there any way to write a type deduction guide for this class?












share|improve this question


















  • 1




    Does is there a way to pass nested initializer lists in C++11 to construct a 2D matrix? help?
    – user4581301
    Nov 20 at 6:12










  • @user4581301 I've seen that question and I don't think that helps with my current situation. In that question the matrix is always 2D, but it's not the case for my Array, and I can't just write as many nested std::initializer_list as I can... Plus that question doesn't help with my second question either.
    – Chlorie
    Nov 20 at 6:28















up vote
5
down vote

favorite












For some reasons I have to implement a multidimensional array class in C++.
The array in question is something like this:



template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
class Array final
{
private:
std::vector<size_t> shape_;
std::vector<T> data_;
public:
// Some public methods
}


T is the type of elements stored in the array, and that the dimensions of the array is not templated since the user should be able to reshape the array, for example:



Array<int> array = Array<int>::zeros(3, 2, 4);
array.reshape(4, 6);


Though implementation of the functions mentioned above went quite smoothly, I stuck at the "beginning" of implementing this class, that is to initialize the array...
My questions are as follows:





  1. Is there any method to have an constructor like this, such that nested initializer lists of different depths create different arrays, like:



    Array<int> a = {1, 2}; // Dimension: 1, Shape: 2
    Array<int> b = {{1}, {2}}; // Dimension: 2, Shape: 2x1


    My approach to implement the constructors made these two arrays the same, which is not what I want. Plus, clang complained about the braced scalars, which seem to be the problem. Currently my naive approach which suffers from the problem above looks like this



    ...
    Array() :data_(0), shape_{0} {}
    Array(std::initializer_list<T> list) :data_(list), shape_{list.size()} {}
    Array(std::initializer_list<Array> lists)
    {
    // Implementation
    }
    ...



  2. It's easy for the compiler to deduct the types for the following arrays:



    Array c = {1, 2}; // T = int
    Array d = {1.0, 2.0}; // T = double


    But I failed to write a working deduction guide for multidimensional ones:



    Array e = {{1, 2}, {3, 4}}; // Expects T = int
    Array f = {{1.0, 2.0}, {3.0, 4.0}}; // Expects T = double


    Is there any way to write a type deduction guide for this class?












share|improve this question


















  • 1




    Does is there a way to pass nested initializer lists in C++11 to construct a 2D matrix? help?
    – user4581301
    Nov 20 at 6:12










  • @user4581301 I've seen that question and I don't think that helps with my current situation. In that question the matrix is always 2D, but it's not the case for my Array, and I can't just write as many nested std::initializer_list as I can... Plus that question doesn't help with my second question either.
    – Chlorie
    Nov 20 at 6:28













up vote
5
down vote

favorite









up vote
5
down vote

favorite











For some reasons I have to implement a multidimensional array class in C++.
The array in question is something like this:



template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
class Array final
{
private:
std::vector<size_t> shape_;
std::vector<T> data_;
public:
// Some public methods
}


T is the type of elements stored in the array, and that the dimensions of the array is not templated since the user should be able to reshape the array, for example:



Array<int> array = Array<int>::zeros(3, 2, 4);
array.reshape(4, 6);


Though implementation of the functions mentioned above went quite smoothly, I stuck at the "beginning" of implementing this class, that is to initialize the array...
My questions are as follows:





  1. Is there any method to have an constructor like this, such that nested initializer lists of different depths create different arrays, like:



    Array<int> a = {1, 2}; // Dimension: 1, Shape: 2
    Array<int> b = {{1}, {2}}; // Dimension: 2, Shape: 2x1


    My approach to implement the constructors made these two arrays the same, which is not what I want. Plus, clang complained about the braced scalars, which seem to be the problem. Currently my naive approach which suffers from the problem above looks like this



    ...
    Array() :data_(0), shape_{0} {}
    Array(std::initializer_list<T> list) :data_(list), shape_{list.size()} {}
    Array(std::initializer_list<Array> lists)
    {
    // Implementation
    }
    ...



  2. It's easy for the compiler to deduct the types for the following arrays:



    Array c = {1, 2}; // T = int
    Array d = {1.0, 2.0}; // T = double


    But I failed to write a working deduction guide for multidimensional ones:



    Array e = {{1, 2}, {3, 4}}; // Expects T = int
    Array f = {{1.0, 2.0}, {3.0, 4.0}}; // Expects T = double


    Is there any way to write a type deduction guide for this class?












share|improve this question













For some reasons I have to implement a multidimensional array class in C++.
The array in question is something like this:



template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
class Array final
{
private:
std::vector<size_t> shape_;
std::vector<T> data_;
public:
// Some public methods
}


T is the type of elements stored in the array, and that the dimensions of the array is not templated since the user should be able to reshape the array, for example:



Array<int> array = Array<int>::zeros(3, 2, 4);
array.reshape(4, 6);


Though implementation of the functions mentioned above went quite smoothly, I stuck at the "beginning" of implementing this class, that is to initialize the array...
My questions are as follows:





  1. Is there any method to have an constructor like this, such that nested initializer lists of different depths create different arrays, like:



    Array<int> a = {1, 2}; // Dimension: 1, Shape: 2
    Array<int> b = {{1}, {2}}; // Dimension: 2, Shape: 2x1


    My approach to implement the constructors made these two arrays the same, which is not what I want. Plus, clang complained about the braced scalars, which seem to be the problem. Currently my naive approach which suffers from the problem above looks like this



    ...
    Array() :data_(0), shape_{0} {}
    Array(std::initializer_list<T> list) :data_(list), shape_{list.size()} {}
    Array(std::initializer_list<Array> lists)
    {
    // Implementation
    }
    ...



  2. It's easy for the compiler to deduct the types for the following arrays:



    Array c = {1, 2}; // T = int
    Array d = {1.0, 2.0}; // T = double


    But I failed to write a working deduction guide for multidimensional ones:



    Array e = {{1, 2}, {3, 4}}; // Expects T = int
    Array f = {{1.0, 2.0}, {3.0, 4.0}}; // Expects T = double


    Is there any way to write a type deduction guide for this class?









c++ arrays multidimensional-array initializer-list






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 20 at 5:28









Chlorie

334




334








  • 1




    Does is there a way to pass nested initializer lists in C++11 to construct a 2D matrix? help?
    – user4581301
    Nov 20 at 6:12










  • @user4581301 I've seen that question and I don't think that helps with my current situation. In that question the matrix is always 2D, but it's not the case for my Array, and I can't just write as many nested std::initializer_list as I can... Plus that question doesn't help with my second question either.
    – Chlorie
    Nov 20 at 6:28














  • 1




    Does is there a way to pass nested initializer lists in C++11 to construct a 2D matrix? help?
    – user4581301
    Nov 20 at 6:12










  • @user4581301 I've seen that question and I don't think that helps with my current situation. In that question the matrix is always 2D, but it's not the case for my Array, and I can't just write as many nested std::initializer_list as I can... Plus that question doesn't help with my second question either.
    – Chlorie
    Nov 20 at 6:28








1




1




Does is there a way to pass nested initializer lists in C++11 to construct a 2D matrix? help?
– user4581301
Nov 20 at 6:12




Does is there a way to pass nested initializer lists in C++11 to construct a 2D matrix? help?
– user4581301
Nov 20 at 6:12












@user4581301 I've seen that question and I don't think that helps with my current situation. In that question the matrix is always 2D, but it's not the case for my Array, and I can't just write as many nested std::initializer_list as I can... Plus that question doesn't help with my second question either.
– Chlorie
Nov 20 at 6:28




@user4581301 I've seen that question and I don't think that helps with my current situation. In that question the matrix is always 2D, but it's not the case for my Array, and I can't just write as many nested std::initializer_list as I can... Plus that question doesn't help with my second question either.
– Chlorie
Nov 20 at 6:28












1 Answer
1






active

oldest

votes

















up vote
1
down vote













The only possible solution that would only involve initializer_list would be to declare a number of constructors that equals the number of possible dimensions:



template<class T>
Array(std::initializer_list<T>)

template<class T>
Array(std::initializer_list<std::initializer_list<T>>)

...


The reason is given in [temp.deduc.call]/1: (P the template parameter)




If removing references and cv-qualifiers from P gives std::initializer_­list [...] and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument [...]
Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context




So if the function parameter is std::initializer_list<T> the nested element of the initializer list argument can not be itself an initializer list.



If you don't want to declare that many constructors, the other option is to explictly specify that the argument is of type std::initializer_list in order to avoid template argument deduction. Below I use a class named "nest" just because its name is shorter:



#include<initializer_list>

using namespace std;

template<class T>
struct nest{
initializer_list<T> value;
nest(initializer_list<T> value):value(value){}
};
template<class T>
nest(initializer_list<T>)->nest<T>;

struct x{
template<class T>
x(initializer_list<T>);
};

int main(){
x a{1,2,3,4};
x b{nest{1,2},nest{3,4}};
x c{nest{nest{1},nest{2}},nest{nest{3},nest{4}}};
}





share|improve this answer



















  • 1




    @PiotrSkotnicki I do know! nest is intended to be used as initializer list, that is just used to initialize an object. The constructor of x is not supposed to store the initializer_list.
    – Oliv
    Nov 20 at 11:31












  • Thanks for your answer! By the way you said that this is "The only possible solution that would only involve initializer_list", what if the question is not restricted to initializer_list?
    – Chlorie
    Nov 22 at 8:19










  • @Chlorie I had thought about a trick involving aggregates initialization and brace elision but I was erroneous. On the other I can't make a logical proof there are no other solutions. Now I seriously doubt there is any.
    – Oliv
    Nov 22 at 8:46













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53386756%2fnested-initializer-list-for-initializing-multidimensional-arrays%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
1
down vote













The only possible solution that would only involve initializer_list would be to declare a number of constructors that equals the number of possible dimensions:



template<class T>
Array(std::initializer_list<T>)

template<class T>
Array(std::initializer_list<std::initializer_list<T>>)

...


The reason is given in [temp.deduc.call]/1: (P the template parameter)




If removing references and cv-qualifiers from P gives std::initializer_­list [...] and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument [...]
Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context




So if the function parameter is std::initializer_list<T> the nested element of the initializer list argument can not be itself an initializer list.



If you don't want to declare that many constructors, the other option is to explictly specify that the argument is of type std::initializer_list in order to avoid template argument deduction. Below I use a class named "nest" just because its name is shorter:



#include<initializer_list>

using namespace std;

template<class T>
struct nest{
initializer_list<T> value;
nest(initializer_list<T> value):value(value){}
};
template<class T>
nest(initializer_list<T>)->nest<T>;

struct x{
template<class T>
x(initializer_list<T>);
};

int main(){
x a{1,2,3,4};
x b{nest{1,2},nest{3,4}};
x c{nest{nest{1},nest{2}},nest{nest{3},nest{4}}};
}





share|improve this answer



















  • 1




    @PiotrSkotnicki I do know! nest is intended to be used as initializer list, that is just used to initialize an object. The constructor of x is not supposed to store the initializer_list.
    – Oliv
    Nov 20 at 11:31












  • Thanks for your answer! By the way you said that this is "The only possible solution that would only involve initializer_list", what if the question is not restricted to initializer_list?
    – Chlorie
    Nov 22 at 8:19










  • @Chlorie I had thought about a trick involving aggregates initialization and brace elision but I was erroneous. On the other I can't make a logical proof there are no other solutions. Now I seriously doubt there is any.
    – Oliv
    Nov 22 at 8:46

















up vote
1
down vote













The only possible solution that would only involve initializer_list would be to declare a number of constructors that equals the number of possible dimensions:



template<class T>
Array(std::initializer_list<T>)

template<class T>
Array(std::initializer_list<std::initializer_list<T>>)

...


The reason is given in [temp.deduc.call]/1: (P the template parameter)




If removing references and cv-qualifiers from P gives std::initializer_­list [...] and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument [...]
Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context




So if the function parameter is std::initializer_list<T> the nested element of the initializer list argument can not be itself an initializer list.



If you don't want to declare that many constructors, the other option is to explictly specify that the argument is of type std::initializer_list in order to avoid template argument deduction. Below I use a class named "nest" just because its name is shorter:



#include<initializer_list>

using namespace std;

template<class T>
struct nest{
initializer_list<T> value;
nest(initializer_list<T> value):value(value){}
};
template<class T>
nest(initializer_list<T>)->nest<T>;

struct x{
template<class T>
x(initializer_list<T>);
};

int main(){
x a{1,2,3,4};
x b{nest{1,2},nest{3,4}};
x c{nest{nest{1},nest{2}},nest{nest{3},nest{4}}};
}





share|improve this answer



















  • 1




    @PiotrSkotnicki I do know! nest is intended to be used as initializer list, that is just used to initialize an object. The constructor of x is not supposed to store the initializer_list.
    – Oliv
    Nov 20 at 11:31












  • Thanks for your answer! By the way you said that this is "The only possible solution that would only involve initializer_list", what if the question is not restricted to initializer_list?
    – Chlorie
    Nov 22 at 8:19










  • @Chlorie I had thought about a trick involving aggregates initialization and brace elision but I was erroneous. On the other I can't make a logical proof there are no other solutions. Now I seriously doubt there is any.
    – Oliv
    Nov 22 at 8:46















up vote
1
down vote










up vote
1
down vote









The only possible solution that would only involve initializer_list would be to declare a number of constructors that equals the number of possible dimensions:



template<class T>
Array(std::initializer_list<T>)

template<class T>
Array(std::initializer_list<std::initializer_list<T>>)

...


The reason is given in [temp.deduc.call]/1: (P the template parameter)




If removing references and cv-qualifiers from P gives std::initializer_­list [...] and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument [...]
Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context




So if the function parameter is std::initializer_list<T> the nested element of the initializer list argument can not be itself an initializer list.



If you don't want to declare that many constructors, the other option is to explictly specify that the argument is of type std::initializer_list in order to avoid template argument deduction. Below I use a class named "nest" just because its name is shorter:



#include<initializer_list>

using namespace std;

template<class T>
struct nest{
initializer_list<T> value;
nest(initializer_list<T> value):value(value){}
};
template<class T>
nest(initializer_list<T>)->nest<T>;

struct x{
template<class T>
x(initializer_list<T>);
};

int main(){
x a{1,2,3,4};
x b{nest{1,2},nest{3,4}};
x c{nest{nest{1},nest{2}},nest{nest{3},nest{4}}};
}





share|improve this answer














The only possible solution that would only involve initializer_list would be to declare a number of constructors that equals the number of possible dimensions:



template<class T>
Array(std::initializer_list<T>)

template<class T>
Array(std::initializer_list<std::initializer_list<T>>)

...


The reason is given in [temp.deduc.call]/1: (P the template parameter)




If removing references and cv-qualifiers from P gives std::initializer_­list [...] and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument [...]
Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context




So if the function parameter is std::initializer_list<T> the nested element of the initializer list argument can not be itself an initializer list.



If you don't want to declare that many constructors, the other option is to explictly specify that the argument is of type std::initializer_list in order to avoid template argument deduction. Below I use a class named "nest" just because its name is shorter:



#include<initializer_list>

using namespace std;

template<class T>
struct nest{
initializer_list<T> value;
nest(initializer_list<T> value):value(value){}
};
template<class T>
nest(initializer_list<T>)->nest<T>;

struct x{
template<class T>
x(initializer_list<T>);
};

int main(){
x a{1,2,3,4};
x b{nest{1,2},nest{3,4}};
x c{nest{nest{1},nest{2}},nest{nest{3},nest{4}}};
}






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 20 at 11:40

























answered Nov 20 at 11:21









Oliv

7,8651853




7,8651853








  • 1




    @PiotrSkotnicki I do know! nest is intended to be used as initializer list, that is just used to initialize an object. The constructor of x is not supposed to store the initializer_list.
    – Oliv
    Nov 20 at 11:31












  • Thanks for your answer! By the way you said that this is "The only possible solution that would only involve initializer_list", what if the question is not restricted to initializer_list?
    – Chlorie
    Nov 22 at 8:19










  • @Chlorie I had thought about a trick involving aggregates initialization and brace elision but I was erroneous. On the other I can't make a logical proof there are no other solutions. Now I seriously doubt there is any.
    – Oliv
    Nov 22 at 8:46
















  • 1




    @PiotrSkotnicki I do know! nest is intended to be used as initializer list, that is just used to initialize an object. The constructor of x is not supposed to store the initializer_list.
    – Oliv
    Nov 20 at 11:31












  • Thanks for your answer! By the way you said that this is "The only possible solution that would only involve initializer_list", what if the question is not restricted to initializer_list?
    – Chlorie
    Nov 22 at 8:19










  • @Chlorie I had thought about a trick involving aggregates initialization and brace elision but I was erroneous. On the other I can't make a logical proof there are no other solutions. Now I seriously doubt there is any.
    – Oliv
    Nov 22 at 8:46










1




1




@PiotrSkotnicki I do know! nest is intended to be used as initializer list, that is just used to initialize an object. The constructor of x is not supposed to store the initializer_list.
– Oliv
Nov 20 at 11:31






@PiotrSkotnicki I do know! nest is intended to be used as initializer list, that is just used to initialize an object. The constructor of x is not supposed to store the initializer_list.
– Oliv
Nov 20 at 11:31














Thanks for your answer! By the way you said that this is "The only possible solution that would only involve initializer_list", what if the question is not restricted to initializer_list?
– Chlorie
Nov 22 at 8:19




Thanks for your answer! By the way you said that this is "The only possible solution that would only involve initializer_list", what if the question is not restricted to initializer_list?
– Chlorie
Nov 22 at 8:19












@Chlorie I had thought about a trick involving aggregates initialization and brace elision but I was erroneous. On the other I can't make a logical proof there are no other solutions. Now I seriously doubt there is any.
– Oliv
Nov 22 at 8:46






@Chlorie I had thought about a trick involving aggregates initialization and brace elision but I was erroneous. On the other I can't make a logical proof there are no other solutions. Now I seriously doubt there is any.
– Oliv
Nov 22 at 8:46




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53386756%2fnested-initializer-list-for-initializing-multidimensional-arrays%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Wiesbaden

Marschland

Dieringhausen