6

I was using gcc 4.8 until I upgraded Ubuntu, now I have gcc-4.9.1-16. Code which used to compile without warnings and run fine now no longer compiles.

static const unsigned WIDTH = 16;
static const unsigned VSCALE = 1;
static const unsigned HEIGHT = WIDTH / VSCALE;
static const unsigned FOOTER = 2;

typedef char Row [WIDTH + 1];
typedef Row World [HEIGHT - FOOTER];

std :: vector <World> m_levels;

World levels [] =
{
    {
        "                ",
        "                ",
        "                ",
        "                ",
        "                ",
        "  ###      ###  ",
        "                ",
        "1122112211221122",
        " 33003300330033 ",
        "1122112211221122",
        " 33003300330033 ",
        "                ",
        "                ",
        "                "
    },
    {
        "       44       ",
        "     555 55     ",
        "    66    66    ",
        "  777      777  ",
        " 66          66 ",
        "  777      777  ",
        "    66#  #66    ",
        "  777 #  # 777  ",
        " 66   #  #   66 ",
        "  777 #  # 777  ",
        "    66#  #66    ",
        "     555 55     ",
        "       44       ",
        "                "
    }
};

// The next line is line 68
m_levels .assign (std :: begin (levels), std :: end (levels));

The final line errors with

.../foo.cpp:68:62: required from here /usr/include/c++/4.9/bits/stl_algobase.h:373:4: error: static assertion failed: type is not assignable

.../foo.cpp:68:62: required from here /usr/include/c++/4.9/bits/stl_construct.h:75:7: error: parenthesized initializer in array new [-fpermissive]

The compile options have not changed, they are -W -Wall -Wextra -Werror -pedantic --std=c++0x as far as I can tell only gcc has changed.

Why does this code no longer compile?

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
spraff
  • 32,570
  • 22
  • 121
  • 229

2 Answers2

6

The bare minimum requirement on value types for standard containers is that they must be Erasable. For the default allocator, this translates to the requirement that given value_type *p;, p->~value_type(); must be well-formed.

If value_type is a class type, it simply invokes the destructor, and is well-formed if the destructor is not deleted and is accessible. If value_type denotes a scalar type, p->~value_type(); is also valid and a no-op (this is called a pseudo destructor call). However, if value_type is an array type, then p->~value_type(); is invalid.

Hence, built-in arrays are never valid value types for standard containers, at least when the default allocator is used. (Various other container operations place more requirements on the value type; built-in arrays would run afoul of at least some of them since they are not assignable.)

Instantiating a standard library template with types that do not satisfy the requirements of that template results in undefined behavior. It appears that libstdc++ by happenstance didn't diagnose your error until the version that shipped with GCC 4.9.

The fix, by the way, is simple. Just use std::array.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • I'm sure what you've said is true in terms of the spec, but I don't understand why it should be so. Isn't the non-erasability of value types merely an arbitrary consequence of defining erasability in terms of operations on *pointers*? It seems to be a constraint of syntax rather than semantics. Right? – spraff Dec 29 '14 at 21:15
  • Also, if arrays are not assignable, how does [the `#if 1` version of this work](http://ideone.com/AsCEWD) but not the `#if 0` version? – spraff Dec 29 '14 at 21:24
  • @spraff Well, yes, it's a syntax constraint. They could have made pseudo destructor calls allowed for arrays of scalar types (and arrays of arrays of scalar types, etc.), but even then it's not going to be very useful because arrays are not assignable (which `vector::assign()` obviously requires), which in turn is arguably a syntax issue rather than semantics... – T.C. Dec 29 '14 at 21:24
  • @spraff Arrays are not assignable. Structs/classes containing arrays are. `std::array` is nothing more than a thin wrapper around a built-in array. – T.C. Dec 29 '14 at 21:25
  • I'm playing around with `std::array` but can't get anything which compiles. What simple fix did you mean? Thanks a lot. – spraff Dec 29 '14 at 21:29
  • @spraff `typedef std::array Row; typedef std::array World;` If you are depending on the array-to-pointer conversion, you'll need to add `.data()` calls. http://coliru.stacked-crooked.com/a/f7a1d05d0e5c97d6 – T.C. Dec 29 '14 at 21:32
  • That's what I had but the initialization doesn't compile: "too many initializers" and "no match for operator=" – spraff Dec 29 '14 at 22:07
  • @spraff If you made `levels` a `std::array` as well, you'll need [an extra pair of braces](http://stackoverflow.com/questions/27669200/how-to-brace-initialize-an-array-of-pairs). – T.C. Dec 29 '14 at 22:13
0

After quite a while, this thread helped me figure out this cryptic error. Some legacy code I had to use also stopped compiling and I could not figure out why. I did not notice I had updated the compiler version in a system upgrade and the new standard lead to

stl_algobase.h:364:4: error: static assertion failed: type is not assignable

My solution was to force it to compile using 03 standard (--std=c++03), instead of the new default. I'll leave this here as an easy fix for those who cannot re-write a lot of code using the proper std::array.

MeloMCR
  • 402
  • 1
  • 6
  • 16