4

I am learning about std::vector in C++. I learnt that a const std::vector<int> means that we cannot change the individual elements within that vector and also cannot append/push_back more elements into it i.e., we only have read-access to the elements, which is expected from an entity that is a const. But i find a difference when defining a const std::vector than defining other const types like int, double etc. The situation is shown below:

int main()
{
    const int i;   //doesn't compiler as expected: initializer needed here 
    const std::vector<int> vec1 ;  //COMPILES FINE: WHY ISN'T AN INITIALIZER NEEDED HERE?

}

As we can see that for a const built in type(like int), we must provide an initializer.

My first question is that why isn't this the case for std::vector. That is, how(why) are we allowed to omit the initializer for a const vector. I mean the const vector means that we won't be able to add elements into it so it seems that this vec1 is useless(practically) now.

So my second question is that is there a use for vec1 ? I mean since the standard allows this so they may have already thought about this case and found out that this vec1 can be useful somewhere. That is, what are the use cases for this vec1 that had no initializer at the time of its definition.

Jason
  • 36,170
  • 5
  • 26
  • 60
  • The formal rule is https://eel.is/c++draft/dcl.init#general-8 My paraphrase is *A const object can't be uninitialized, which means you can't reach the ["no initialization is performed"](https://eel.is/c++draft/dcl.init#general-7.3) outcome when following the rules for default initialization.* – Ben Voigt Apr 25 '22 at 15:33
  • 1
    vec1 isn't allowed by the standard because it, specifically, is "useful". It is allowed because: (1) default constructors exist and (2) const exists. And, yes, both default constructors and const qualifiers are tremendously useful. – Matthew M. Apr 25 '22 at 15:42
  • 4
    You assume that standard went out of its way to allow empty vectors. That isn't true. The answer explains what happens - object must be initialized. Class objects can't go uninitialized, so they can always be declared `const` without explicit initializer. There was no discussion if they should allow or disallow empty vectors, it was just about `const` primitive types - those can't have *any* use at all if not explicitly initialized (or if they are not global). One use for empty `const` vector would be naming it - so that you could say e.g. `return EMPTY;` instead of `return std::vector{}` – Yksisarvinen Apr 25 '22 at 15:42
  • 1
    @Yksisarvinen I assumed because i didn't know. To confirm my assumption i asked a question so that experts can tell me if and where/why exactly my assumption is wrong and i can learn from that. That is the whole point of asking a question. An assumption is not the same as a claim. Anyways, thanks for mentioning the use case of `return EMPTY;`. – Jason Apr 25 '22 at 15:50
  • @Anya: While that's a valid point, one should never assume that the fact that something happens means that someone specifically *intended* for it to happen or be materially useful. This is especially true of programming and other rules-based domains where emergent properties can occur due to rules interactions. – Nicol Bolas Apr 25 '22 at 16:00

1 Answers1

9

As we can see that for a const built in type(like int), we must provide an initializer.

That is incorrect. You must ensure that a const object is initialized.

If you do not provide an initializer for an object, that object will undergo the process of default initialization:

To default-initialize an object of type T means:

  • If T is a (possibly cv-qualified) class type ([class]), constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one for the initializer () is chosen through overload resolution ([over.match]). The constructor thus selected is called, with an empty argument list, to initialize the object.
  • If T is an array type, each element is default-initialized.
  • Otherwise, no initialization is performed.

vector<int> is a class type, so the first option is used. The default constructor will be called to initialize it.

int is not a class type. It is also not an array type. Therefore, no initialization is performed. And if this happens to a const object, the code is ill-formed.

For initializing const object, further rules exist:

If a program calls for the default-initialization of an object of a const-qualified type T, T shall be a const-default-constructible class type or array thereof.

int is not a class type or array of class types at all. So any attempt to create a const-qualified object of type int with default-initialization violates this rule and is ill-formed.

vector<int> happens to follow the rules of const-default-constructible, which is why your code works.

As to whether this particular object is "useful", that's up to you. It's an empty vector<int> which cannot be made non-empty. If that's useful to you in some circumstance, then it is useful.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • This is the right rationale, but misses the actual rule. – Ben Voigt Apr 25 '22 at 15:37
  • @BenVoigt: Are you talking about "const-default-constructible"? Because that doesn't really apply here. It's not what causes the `int` version to error out. – Nicol Bolas Apr 25 '22 at 15:39
  • 1
    Certainly it is. "If a program calls for the *default-initialization* of an object of a `const`-qualified type `T`, `T` shall be a *const-default-constructible* class type or array thereof." When `T` is `int`, it is not a const-default-constructible class type (it is not any class type at all) and also is not an array of one. Therefore it is not allowed. – Ben Voigt Apr 25 '22 at 16:10
  • Your answer currently claims "You must ensure that a const object is initialized" without a Standard citation, that's where you should be citing this rule. – Ben Voigt Apr 25 '22 at 16:11