1

I would like to have structs holding enums for iteration, together with std::string holding their names for creating menu entries. I was using something like this:

struct S
{
    enum E { ONE, TWO, ALL };
    std::array<std::string, ALL> names { "one", "two" };
};
int main()
{
    S s;
    for(int i=s.ONE; i<s.ALL; ++i) // or S::...
        std::cout << s.names[i] << '\n';
    return 0;
}

As I understand, this is preferred to having global variables. It works, but needs instantiation before usage. Now, I found out about this method, which needs --std=C++17 to compile:

struct S
{
    enum E { ONE, TWO, ALL };
    static constexpr std::array<std::string_view, ALL> names { "one, "two" };
};
int main()
{
    for(int i=S::ONE; i<S::ALL, ++i)
        std::cout << S::names[i] << '\n';
return 0;
}

But how will this behave, in terms of memory usage, compared to my previous way of doing this? Or is there a wrong way in how I do it? What would be a better way?

a concerned citizen
  • 787
  • 2
  • 9
  • 25

2 Answers2

2

It's not completely clear what you ask, but except from a few small errors in your example it's fine.

If we change the example a bit to not use iostream (because it's confusing with that much code in the assembly listing). An example, instead summing the size of the names:

#include <string_view>
#include <array>

struct S
{
    enum E { ONE, TWO, ALL };
    static constexpr std::array<std::string_view, ALL> names { "one", "two" };
};
int main()
{
    int l=0;
    for(int i=S::ONE; i<S::ALL; ++i){
        l+= S::names[i].size() ;                 
    }

return l;
}

It compiles to a few assembly instructions representing "return 6". But, as Galik points out, no need for the enum trick.

https://godbolt.org/g/ot2kpt

You can also pick a range to loop over, like this:

for(auto n = std::begin(S::names)+1; n != std::end(S::names); ++n)
Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
2

You don't need that enum when using std::array. You can do this:

struct S
{
    static constexpr std::array<std::string_view, 2> names { "one", "two" };
};

int main()
{
    for(auto name: S::names)
        std::cout << name << '\n';
}

Or this:

int main()
{
    for(auto n = std::begin(S::names); n != std::end(S::names); ++n)
        std::cout << *n << '\n';
}

This is very different from your first example though because now you only have one array for every sruct S. Before, every struct has its own copy of the array.

So do what suits your need. One array per object? Or one array for all your objects?

Galik
  • 47,303
  • 4
  • 80
  • 117
  • Unfortunately, it's not always that I need all the entries, some may be skipped, so I don't always use the `for()` loop, or sometimes it's limited `for(int i=S::THREE; i – a concerned citizen Apr 01 '18 at 12:50
  • @aconcernedcitizen You can do: `for(auto i = std::begin(S::names) + 2; i < std::begin(S::names) + 6; ++i) {}` – Galik Apr 01 '18 at 12:52
  • Of course I would rather minimize the memory usage, but it may not be as readable, that's why the `enum`. Still, despite using `std::begin` and `std::end`, you seem to use a `struct`, as well, you just drop the `enum`. If I use `enum` and then, as you suggest, `for(auto i=std::begin(S::names)+S::TWO; ...)`, would that be as effective as only having one array in memory, no matter how many `for()` loops are calling it? – a concerned citizen Apr 01 '18 at 12:57
  • @aconcernedcitizen To be honest I am not sure that `std::begin(S::names) + S::TWO` is any clearer than `std::begin(S::names) + 2`. But its up to you. – Galik Apr 01 '18 at 13:00
  • I might also have some doubts, but it's more along the lines of `S::LEGENDRE`. Still, this seems a good way to minimize having too many objestc (which I was trying to avoid with thi snew method, rather than instantiating an object for each case). – a concerned citizen Apr 01 '18 at 13:04