0

So I was messing around with some code and I decided that I wanted some sort of list of strings... so then I thought - I can't be bothered to figure out what types to use and so on, so I would just whack it into an auto variable, like this:

static auto constexpr t = {"red", "black", "green"};

and the code compiled. Great, so since this:

static auto constexpr str = "green";

derives to a char[] I was assuming that {"red", "black", "green"} might be a *char[] or some such, thus I could write:

std::cout << "value 1 is: " << t[1] << std::endl;

Which gives me the error:

main.cpp:18:56: error: no match for ‘operator[]’ (operand types are ‘const std::initializer_list’ and ‘int’)

So I presume the type is "initializer_list"? Is there a way I can do somthing like: t[1] to get at the string "black" (assuming index starts at 0)?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
code_fodder
  • 15,263
  • 17
  • 90
  • 167

2 Answers2

1

So I presume the type is "initializer_list"?

Yes.

Is there a way I can do somthing like: t[1] to get at the string "black" (assuming index starts at 0)?

Use std::array, which plays nicely with C++17 class template type deduction:

static auto constexpr t = std::array{"red", "black", "green"};

std::cout << t[1] << "\n"; // outputs 'black'

constexpr-ness is preserved if you pass compile time constants to operator[] or std::get.

constexpr const char* black = t[1];
constexpr const char* red = std::get<0>(t);
lubgr
  • 37,368
  • 3
  • 66
  • 117
  • ah ha, that's pretty cool : ) ... I was just reading about init..._list and I also saw you can do the ugly: `t.begin()[1]` – code_fodder Sep 27 '18 at 12:55
1

An object of type std::initializer_list is a lightweight proxy object that provides access to an array of objects of type const T. (https://en.cppreference.com/w/cpp/utility/initializer_list)

It doesn't have an operator[] member function, though, the only accessors beeing std::initializer_list::begin and std::initializer_list::end. E.g.:

#include <iostream>
#include <initializer_list>
#include <stdexcept>

namespace my {

template <typename T>
constexpr auto get(std::initializer_list<T> lst, std::size_t i)
{
    if ( i >= lst.size() )
        throw std::out_of_range("Out of bounds access.\n");
    return *(lst.begin() + i);   
}

template <std::size_t I, typename T>
constexpr auto get(std::initializer_list<T> lst) 
{
    // Note: https://stackoverflow.com/questions/5438671/static-assert-on-initializer-listsize
    if ( I >= lst.size() )
        throw std::out_of_range("Out of bounds access.\n");
    return *(lst.begin() + I);   
}

}

int main()
{
    // Note: https://stackoverflow.com/questions/16063123/is-it-legal-to-declare-a-constexpr-initializer-list-object
    static constexpr auto t = {"red", "black", "green"};

    std::cout << my::get(t, 1) << '\n' << my::get<2>(t) << '\n';
}
Bob__
  • 12,361
  • 3
  • 28
  • 42