1

I have the following code in test.cpp:

#include <vector>
#include <string>

class A {
public:
  static const std::vector<std::string> foo;
};
const std::vector<std::string> A::foo {{"bar", "baz"}};

int main() {}

It compiles, but when I run it, I get the following error:

terminate called after throwing an instance of 'std::length_error'
  what():  basic_string::_S_create
Aborted (core dumped)

Why am I getting this error?

Hopefully irrelevant: I'm using g++ 4.8.2, with -std=c++11.

Aside: I purposely initialized foo outside the class. If I do it inside the class, the compiler tells me that an out-of-class initialization is required (which is ridiculous, imo).

Brandon
  • 1,336
  • 3
  • 10
  • 38
  • you should post the actual code, that code does not even compile (I didn't downvote, btw) – ichramm Feb 16 '15 at 04:11
  • Yes, it does compile. I had an issue where I forgot `A::foo`, and I edited the question. But it still compiled before that edit. I copied and pasted the actual code. And the question still stands, btw. Even with `A::foo` I get the same error. – Brandon Feb 16 '15 at 04:13
  • 3
    Don't use double braces. – T.C. Feb 16 '15 at 04:14
  • @T.C. - wow! What a ridiculous issue. Why doesn't the compiler catch that? – Brandon Feb 16 '15 at 04:15
  • Seems inefficient for the compiler to defer it to runtime, it should recognize `std::string` as a special case, or something. – Brandon Feb 16 '15 at 04:20
  • 1
    You can reduce your problem to `int main() { std::string s = {"bar", "baz"} ; }` – quantdev Feb 16 '15 at 04:20
  • Uniform initialization: swap one bunch of problems for another bunch of problems :) – M.M Feb 16 '15 at 04:25

2 Answers2

8

Fact #1: a string literal, which is const char [N], can decay into a const char *.

Fact #2: std::string has a constructor taking two iterators.

Fact #3: A pointer is an iterator.

Result: In

const std::vector<std::string> A::foo {{"bar", "baz"}};

the initializer can be parsed in two different ways:

  1. {"bar", "baz"} is the initializer for a single std::string using the two-iterator constructor. Outer braces create an initializer_list<std::string>
  2. "bar" and "baz" are initializers for two std::strings. {"bar", "baz"} creates an initializer_list<std::string>. Outer braces are redundant.

You want #2. The standard says the compiler must pick #1. Havoc ensues.

T.C.
  • 133,968
  • 17
  • 288
  • 421
3

The problem is in the vector initialization:

const std::vector<std::string> A::foo {{"bar", "baz"}};

You cannot use double brackets, I don't even know what constructor is calling there (I guess is trying to create a vector from a char* array that doesn't end)...

This one should work (note I removed some brackets):

const std::vector<std::string> A::foo {"bar", "baz"};
ichramm
  • 6,437
  • 19
  • 30