-3

I don't understand what clarification is needed. Please explain your concerns. From my viewpoint, I have asked a question which conforms to the technical area being addressed, I have explained my confusion, I have given an example which illustrates my issues, and I have elaborated on the diagnostic messages received. The respondents to this question understood my question, the example, and the diagnostic messages in sufficient detail that they were able to respond appropriately and to correct my errors and misconceptions. In what way can this dialog be made more pungent with an appropriately asked question?

@Ruzihm stated that there were no lists, I suppose in my example. I would like to explain why that statement is erroneous. Years ago there was a LIFO stack and a FIFO stack. We call a LIFO stack a 'stack', and a FIFO stack a 'queue'. In England, the line standing before, e.g., a theater box office is a queue, and the pigtails that Chinese men wore after the Mongol conquest of China is also a queue. It all depends on context.

The complaint that there are no 'lists' probably extends from the notion that there are no instantiations of <list> objects. But such objects are more appropriately called a linked list, a linked list of linked lists is known as a graph, see e.g. Gnu gSlip documentation. Clearly linked lists were not involved.

In C/C++ it is possible to form a multiple-dimensional array which are non-rectangular. Such objects have historically been known as a 'list of lists', and not an 'array of arrays'. The reason is clear, a 'list' is an object containing items, in programmatic terms, accessible by an index, as in list[index]. This is not to say that a linked list, a.k.a., <list>, is not also a 'list', but normal convention makes more explicit that a <list> is a linked list.

If the question is changed to 'array of arrays', then the examples make little sense. In terms likened to using <list>, there are no <array>'s. So whatever criticism exists for using 'list of lists' is retained and relevant in using 'array of arrays'. If one is inappropriate, the other is also.

The answer given, i.e., use a vector of vectors, is to the point, accurate, and responds correctly to the question about a list of lists. As stated in documentation concerning vectors, for example on https://cpluscplus.com, a vector is a list where elements are accessible by index. In this context, a vector of vectors is a particular list of lists, more particularly, a list of lists which are jagged and not an array of arrays which must be rectangular.

I believe any statement saying that the example posted to explain my quandry in forming a list of lists is inappropriate shows some misunderstanding of software practice and jargon. The question is correctly formed and the examples correctly address the question.

If this question is deleted, or in other ways made non-accessible, then you support an ill-formed statement concerning the question. I hope that this is not done.

MSVS 16.9.1

I've created a list of lists of strings. I have difficulty getting either the size of the list of lists or the size of string array in the list. I get conflicting error messages and I don't know how to resolve them.


# include <string>

using namespace std;

static const string   x[3] = { "a", "b", "c" };
static const string   y[]  = { "a", "b", "c" };
static const string*  z[]  = { x, y };
int xSize  = x->size();
int ySize  = y->size();
int zSize  = z.size();    // expression must have class type
int zpSize = z->size();   // expression must have pointer-to-class type
int xzSize = z[0]->size();
int yzSize = z[1]->size();

The error descriptions are:

Expression must have class type error in C++ Last Updated : 14 Jan, 2021 The expression must have class type is an error that is thrown when the dot (.) operator is used to access an object’s properties, is used on pointers to objects.

And:

Well here we have a problem. My MSVS uses bing to do a web search for the answer, and none of the answers are as pointed as the one above. The gist is that the object must be a pointer and it is not.

The upshot is that neither zpSize nor zSize work and the errors given are contradictory. I'm stumped.

lostbits
  • 928
  • 1
  • 9
  • 23
  • 2
    There aren't any lists in this question... – Ruzihm Mar 17 '21 at 18:59
  • better dont try to guess how it works. C-arrays have no member `size()` – 463035818_is_not_an_ai Mar 17 '21 at 19:05
  • Also, when you call `x->size()` and `y->size()`, you are calling the `string::size` function on the first element of the array. `z` is an array of pointers, which isn't a class and can't be implicitly converted to a class, hence the **expression must have class type** error. It also isn't a pointer to a class and can't be implicitly converted to a pointer to a class, hence the **expression must have pointer-to-class type** error. – Ruzihm Mar 17 '21 at 19:05
  • You can use [`std::extent`](https://en.cppreference.com/w/cpp/types/extent) to find out the size of an array, but using `std::array` instead is usually easier. – François Andrieux Mar 17 '21 at 19:13
  • You can't use an array as container items. Look here for some explanations https://stackoverflow.com/questions/826935/how-do-i-store-arrays-in-an-stl-list – Valeca Mar 17 '21 at 19:23
  • @FrançoisAndrieux or better, [`std::size()`](https://en.cppreference.com/w/cpp/iterator/size), in this case. `std::size()` operates on an array *instance*, whereas `std::extent` operates on an array *type*. – Remy Lebeau Mar 17 '21 at 19:28
  • @RemyLebeau Thanks, I wasn't aware of that feature. TIL – François Andrieux Mar 17 '21 at 19:34

2 Answers2

2

Best to avoid low level constructs like C-Arrays when learning (there are a few to many gotchas that are not obvious).

Use the C++ containers (either std::list or std::vector or std::array) and you will get the behavior your want:

static const std::list<std::string>   x = { "a", "b", "c" };
static const std::list<std::string>   y = { "x", "y", "z" };

int xSize  = x.size();
int ySize  = y.size();

A list of a list gets a bit more complex. Not because it is harder but what you want is actually a list of referecnes to other lists. Making a list of lists is easy.

// A list of lists
std::list<std::list<string>> z = {{ "a", "b", "c" }, { "x", "y", "z" }};
int zSize  = z.size();

But if you want to keep the original lists as well as the new list:

std::list<std:: reference_wrapper<const std::list<string>> z1 = {x,y};
int zSize1  = z1.size();

The trouble with your current code:

static const string   x[3] = { "a", "b", "c" };

Here x is a C-Array of std::string. The problem with that is that arrays can easily decay into pointers. And that is what is happening here:

x->size(); // the array is converted into a pointer (to the first element).
           // So this will return the size of the string "a"
           // Which is 1.

What you should have done is:

x[0].size();

This is a good reason to use C++ containers rather than low level structures like C-Arrays. If you want a fixed size array you could use std::array<> and that achieves the same as the C-Array (and has the same foot print) but has all the protection of being a fully fledged class.


Looking at your access of z:

static const string*  z[]  = { x, y };

So z is a C-Array of pointers to std::string pointers. This has more issues as it is unclear on the memory management (if I was not looking at exactly this part of the code).

But you can create z here because both x and y decay from arrays into pointers automatically.

The statement:

Getes the first element of z which is a pointer to the first element in X and then calls the size() member of that object.

int xzSize = z[0]->size();

C-Arrays do not have members so you can not get their size. Technically you can calculate their size:

int xSize = sizeof(x) / sizeof(x[0]);

But if you do this to a pointer it will not work:

// This will fail as z stores the pointer not the array.
int xzSize = sizeof (z[0]) / sizeof(z[0][0]);
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • @Remy Lebeau thanks to both of you.. I am less confused but still addled. – lostbits Mar 17 '21 at 20:21
  • @ArthurSchwarez Avoid the C-Array. If you need arrays use the C++ `std::array` this will behave exactly as you would expect and has no extra cost. If you want a more versatile container use `std::vector<>` most of the time `std::list<>` if you have a specific use case. When you get more experienced you can start mixing in the C-Array. – Martin York Mar 17 '21 at 20:39
2

x and y are fixed arrays of 3 string elements. Referring to a fixed array by its name alone will decay into a pointer to the array's first element. Thus, x->size() is the same as (&x[0])->size(), or simply x[0].size(). Same with y->size(). So, you are getting the size of the 1st strings in the arrays, not the size of the arrays themselves. For that, you can use std::size() or std::extent instead, eg:

size_t xSize = std::size(x);
size_t ySize = std::size(y);
size_t xSize = std::extent<decltype(x)>::value;
size_t ySize = std::extent<decltype(y)>::value;

z is also a fixed array, of 2 string* elements. A fixed array is not an instance of a class, so you can't call any member methods on it, which is why z.size() and z->size() are illegal. Again, use std::size() or std::extent instead, eg:

size_t zSize = std::size(z);
size_t zSize = std::extent<decltype(z)>::value;

Also, note that due to array decay, z is holding string* pointers to the 1st strings in x and y, not pointers to x and y themselves. Thus xzSize and yzSize will, again, be the sizes of the 1st strings in the x and y arrays, not the sizes of x and y themselves. If you want that, you need to make z point at x and y directly, eg:

#include <string>

using namespace std;

typedef string stringArr3[3];

static const stringArr3 x = { "a", "b", "c" };
static const stringArr3 y = { "a", "b", "c" };
static const stringArr3* z[] = { &x, &y };
int xSize  = std::size(x);
int ySize  = std::size(y);
int zSize  = std::size(z);
int xzSize = std::size(*(z[0]));
int yzSize = std::size(*(z[1]));

Now, that being said, consider using std::array instead, which has a size() member method, eg:

#include <string>
#include <array>

using namespace std;

using stringArr3 = array<string, 3>;

static const stringArr3 x{ "a", "b", "c" };
static const stringArr3 y{ "a", "b", "c" };
static const array<stringArr3*, 2> z{ &x, &y };
size_t xSize  = x.size();
size_t ySize  = y.size();
size_t zSize  = z.size();
size_t xzSize = z[0]->size();
size_t yzSize = z[1]->size();
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • That is really helpful, thanks. My actual solution requires thjat I use variable sized arrays of strings. I'll try to hunt up an answer if you don't have time. The 'truth' is that I've been coding C++ since '95 but in the last few years have 'drifted' to other languages. I/m rusty as well as old. Thanks – lostbits Mar 17 '21 at 23:11
  • Pardon. I mean fixed sized arrays of variable size. No two arrays have the same size, however, each array's size will not change. I could hand calculate that actual size, as in string x[size] = {}, and then array, but I wonder if there's another way. – lostbits Mar 17 '21 at 23:33
  • @ArthurSchwarez "*My actual solution requires thjat I use variable sized arrays of strings*" - then use `std::vector` for that. – Remy Lebeau Mar 18 '21 at 01:05
  • @ArthurSchwarez "*as in string x[size] = {}, and then array*" - neither of those will work. The first is a Variable-Length Array, which is [not standard or portable](https://stackoverflow.com/questions/1887097/), and the second won't compile. You need compile-time constants for both types of array. If an array's size is only known at run-time, use `std::vector` for the array. – Remy Lebeau Mar 18 '21 at 01:06
  • Problem solved. As suggested, vector x(list of things); and vector> z { list of vectors }. I haven't started to debug but there are no syntax errors and I think it should work. I use ``` for (vector name: z) in the code. – lostbits Mar 18 '21 at 19:12
  • @ArthurSchwarez note that `for (vector name: z)` initializes `name` **by value**, so it will make a *copy* of each `vector` in `z`. You should typically use references instead when iterating a container of non-trivial elements, eg: `for (vector &name: z)` Or simpler: `for (auto &name: z)` – Remy Lebeau Mar 18 '21 at 19:13