0

It is easy to fill the elements of a vector by index after it has been initialized:

std::vector<int> myVector {0, 0, 0};
int indexOfInterest = 1;
myVector[indexOfInterest] = 999;
// myVector now contains {0, 999, 0}

However, is there a way to initialize a vector directly with the index? indexOfInterest may change in my code in the future, and I would like to avoid hard-coding the vector with

std::vector<int> myVector {0, 999, 0};

Is there some syntax like

int indexOfInterest = 1;
std::vector<int> myVector[3] {indexOfInterest : 999}; // this is made-up syntax!
// desired: myVector contains {0, 999, 0} and is 3 elements large

that can be used in C++11 to achieve this effect?

xyzhou
  • 39
  • 1
  • 7
  • 4
    `std::vector myVector[3]` is an array of size 3 of `std::vector`. is this a typo? In the first snippet is it just a `std::vector` – 463035818_is_not_an_ai Feb 19 '20 at 16:47
  • Why don't you initialize all values to `999`? – NutCracker Feb 19 '20 at 16:48
  • `{indexOfInterest : 999}` what exactly is this syntax supposed to do? – Borgleader Feb 19 '20 at 16:49
  • 2
    No there is no way to do it (yet) – Slava Feb 19 '20 at 16:51
  • What exactly is your issue with the first code example? The only issue I see with it is the repetition of the zero elements, which can be avoided by using `std::vector myVector(3);` instead. – walnut Feb 19 '20 at 16:51
  • @walnut issue is it would be more readable to initilize vector with all default values except some at particular indexes. There is a video about that but I do not have access to youtube from work – Slava Feb 19 '20 at 16:52
  • @Slava If that is all, one can write a function to create such a vector. – walnut Feb 19 '20 at 17:02
  • @idclev463035818 I am trying to communicate that the vector should be 3 elements large. I understand that this syntax is incorrect. – xyzhou Feb 19 '20 at 17:10
  • 1
    thats what i was suspecting, but if you make up some syntax you should explain carefully what it is supposed to mean. In this particular case there is no way the made up syntax can do what you want, because as mentioned that would be an array of 3 vectors ;) – 463035818_is_not_an_ai Feb 19 '20 at 17:13
  • @Borgleader I am pulling from python-esque dictionary initializations, where the index at indexOfInterest is initialized with value 999. – xyzhou Feb 19 '20 at 17:14
  • @walnut the issue is that the first example cannot work in an initialization list. I need all elements to default to zero EXCEPT the one at indexOfInterest, which may change in future code. – xyzhou Feb 19 '20 at 17:14
  • Here's a [non-ideal, but good, work-around that I like to use.](https://stackoverflow.com/a/70889843/4561887) – Gabriel Staples Jan 28 '22 at 07:04
  • @Borgleader, the desired pseudo-syntax of `{indexOfInterest : 999}` would set the `std::vector`'s value at index `indexOfInterest` to `999`. I can totally see the point and desire here of what the OP is asking. Coming from C, where this exact type of feature exists (see [my answer](https://stackoverflow.com/a/70889843/4561887) for details), I was hoping something like this would exist in C++ too for C-style arrays, `std::array`, or `std::vector`. – Gabriel Staples Jan 28 '22 at 07:18
  • @GabrielStaples why are you pinging me about a 2 yr old comment that was already addressed? – Borgleader Jan 28 '22 at 17:06

5 Answers5

2

Another way of doing this would be to use std::unordered_map. Not quite the same thing, but could work for you depending on what you're doing:

std::unordered_map<int, int> mp{ { indexOfInterest, 999} };
mp[someOtherIndex] = 42;

All the indexes you haven't assigned anything to will have the default value (0 in this case) when you try to access them.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
2

No there is no such thing in C++11. If it is only a single index that needs a different initializer than all the others, I wouldn't use anything other than

std::vector<int> myVector(3);
myVector[1] = 999;

I think it wouldn't be too difficult to write some fake iterator class that lets you write something along the line of

auto pit = pair_of_fake_iterators(3,1,999);
std::vector<int> myVector( pit.first, pit.second);

Which calls the constructor that takes two iterators and "copies" elements. Though, I wouldn't go there. It saves you nothing on typing and readers of your code will applaude for the level of obfuscation.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
0

As long as you want everything in the vector to be the same value except for the one value you want different, you can use

std::vector<int> myVector(size_of_vector, /* some_value_here_if_you_do_not_want_0 */);
myVector[index_you_care_about] = value;
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
0

In the comments you mention, that you need this in order to initialize the vector in a member initializer list.

You can delegate the construction to a function or lambda in the member initializer list:

some_constructor(...) : myVector([]{
    std::vector<int> vec(3);
    myVector[indexOfInterest] = 999;
    return vec;
}()) {
}

You can also write yourself a generic function for the task, e.g.:

template<typename T, std::size_t N>
std::vector<T> make_vector(typename std::vector<T>::size_type size, const T& value, std::pair<typename std::vector<T>::size_type, T>(&&entries)[N]) {
    std::vector<T> vec(size, value);
    for(auto& entry : entries)
        vec[entry.first] = std::move(entry.second);
    return vec;
}

template<typename T, std::size_t N>
std::vector<T> make_vector(typename std::vector<T>::size_type size, std::pair<typename std::vector<T>::size_type, T>(&&entries)[N]) {
    return make_vector<T>(size, {}, entries);
}

This can be used in the member initializer for example like this:

some_constructor(...) : myVector(make_vector(3, 0, {{indexOfInterest, 999}})) {
}

or

some_constructor(...) : myVector(make_vector<int>(3, {{indexOfInterest, 999}})) {
}

with the second overload.

Instead of an array, you can also take the argument as a std::initializer_list<...> with the difference being that the array version instantiates new functions for each list size given to the call, while the initializer list will use the same function for different list sizes.

The function does in any case not copy the constructed vector. It may however move it once (C++17, due to mandatory copy elision except for vec into the return value) or multiple times (before C++17). All these may however be optimized away by the compiler due to (named) return value optimization.

walnut
  • 21,629
  • 4
  • 23
  • 59
0

Is there a way to initialize a vector by index...

In C there is: see What do square brackets mean in array initialization in C? , with something called "Designated Initializers".

In C++ there is not, but I have presented a work-around I like, below. See cppreference.com here, under the "Designated initializers" section near the bottom: https://en.cppreference.com/w/cpp/language/aggregate_initialization

Note: out-of-order designated initialization, nested designated initialization, mixing of designated initializers and regular initializers, and designated initialization of arrays are all supported in the C programming language, but are not allowed in C++.

struct A { int x, y; };
struct B { struct A a; };
struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ (out of order)
int arr[3] = {[1] = 5};        // valid C, invalid C++ (array)
struct B b = {.a.x = 0};       // valid C, invalid C++ (nested)
struct A a = {.x = 1, 2};      // valid C, invalid C++ (mixed)

See also the GCC documentation here (emphasis added):

In ISO C99 you can give the elements in any order, specifying the array indices or structure field names they apply to, and GNU C allows this as an extension in C90 mode as well. This extension is not implemented in GNU C++.

And see also my question here: Initializing std::vector with square brackets [] inside; what is happening?

So, you can use std::map or std::unordered_map to do this, but I want really fast access, and accessing a contiguous piece of memory such as a C-style array, C++ std::array, or C++ std::vector via a simple index will be much faster. So, what I have settled on as a work-around solution is this:

In header.h:

WARNING: be sure to set the size! operator[] for std::vector does NOT automatically add members if you index past the end of the vector. See: https://en.cppreference.com/w/cpp/container/vector/operator_at:

(Regarding std::vector<T,Allocator>::operator[]; emphasis added):

Unlike std::map::operator[], this operator never inserts a new element into the container. Accessing a nonexistent element through this operator is undefined behavior.

#include <vector>

std::vector<int> v(5); // WARNING: set the size you need!

In source.cpp:

v[0] = 1;
v[1] = 2;
v[2] = 3;
v[3] = 4;
v[4] = 5;

This is not ideal because it doesn't allow you to make your vector const or constexpr, but that's okay. It's a worthwhile tradeoff I think in the case of trying to make fast maps with crystal clear mapping. Ex:

(Use a C style enum here, NOT a C++ style enum class--more on that in my answer here: How can I iterate over an enum?)

enum fruits
{
    FRUITS_APPLE = 0,
    FRUITS_BANANA,
    FRUITS_PEAR,
    FRUITS_LEMON,
    FRUITS_KIWI,
    /// Not a valid value; this is the number of members in this enum
    FRUITS_count,
};

// Now you can initialize your std::vector<> in your .cpp file clearly
// (with crystal-clear enum-based indexing, or mapping) like this. 
// Think of this as a key:value pair, with the enum being the key and
// the value of course being the value.
v[FRUITS_APPLE]  = 1;
v[FRUITS_BANANA] = 2;
v[FRUITS_PEAR]   = 3;
v[FRUITS_LEMON]  = 4;
v[FRUITS_KIWI]   = 5;

Note that if the object is a fixed size, you are better off using std::array instead:

Declaration of the array, to map from enum fruits to integers, for example:

#include <array>

std::array<int, FRUITS_count> fruitsArray;

Load it with values:

fruitsArray[FRUITS_APPLE]  = 1;
fruitsArray[FRUITS_BANANA] = 2;
fruitsArray[FRUITS_PEAR]   = 3;
fruitsArray[FRUITS_LEMON]  = 4;
fruitsArray[FRUITS_KIWI]   = 5;

Side note: if you ever need to make the data type constexpr, std::array and C-style arrays can both be made constexpr, but std::vector canNOT--it can only be const at best.

See also:

  1. [my answer] How can I iterate over an enum?
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265