0

First off, my code compiles and runs fine on Mac OS X with compiler

i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1

but on Ubuntu with compiler

g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

it won't compile.

In my header I have

std::vector<Obstacle::Obstacle*> obstacles;

which gives the following compilation error in Ubuntu:

error: ‘Obstacle::Obstacle’ cannot appear in a constant-expression

Any ideas of what I need to change to make it work? Is there some magical compile flag I should use on Ubuntu to make it work just as on OS X?

Thank you

EDIT: Obstacle is a class.

Isak T.
  • 335
  • 1
  • 3
  • 16
  • 2
    What is this `Obstacles::Obstacles` thing? Please show the definition. Your question as is requires us to read your invisible code / read your mind. – David Hammen Oct 14 '12 at 15:24
  • If you're new to C++ and using an `std::vector` of pointers, there's a good chance you'll end up with a memory leak (assuming you `new` the objects being pushed into the vector). You need to make sure you `delete` every vector element before it goes out of scope. But, instead of going through all this trouble, it is better to use `std::vector>` instead. – Praetorian Oct 14 '12 at 15:40
  • @Prætorian thanks for the comment, it was discussed in [this](http://stackoverflow.com/q/12880803/251666) question from earlier today. – Isak T. Oct 14 '12 at 16:05

2 Answers2

4

Apparently Obstacle is just a class.

Amazingly, the following also works, tested in g++ 4.2, g++ 4.3, g++ 4.4, clang++ 2.9, and clang++ 3.1:

std::vector<Obstacle::Obstacle::Obstacle::Obstacle::Obstacle*> obstacles;

Multiple versions of g++ and multiple versions of clang compiled the above.

g++ 4.5 and 4.6 have problems with this construct. This looks like a g++ bug, versions 4.5 and higher. So why should this be legal?

This is a bug in pre 4.5 g++, clang, and apparently other compilers. The relevant portion of the standard is 3.4.3.1, para 1a:

If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (clause 9), the name is instead considered to name the constructor of class C. Such a constructor name shall be used only in the declarator-id of a constructor definition that appears outside of the class definition.

In other words, Obstacle::Obstacle is illegal except when used in an out of line definition of a constructor for class Obstacle.

So how are these compilers parsing this? Those compilers are treating Obstacle::Obstacle as having special meaning only in the case of an out of line definition of a constructor. Otherwise, Obstacle::Obstacle follows the injected name rules, but ignore the fact that that rule does not apply here. Obstacle::Obstacle* isn't a pointer to the constructor because constructors don't have names. Obstacle::Obstacle* instead means whatever Obstacle* means when evaluated from within the context of the class Obstacle. But inside the class, Obstacle* is still a pointer to an instance of class Obstacle. Obstacle::Obstacle* is just an Obstacle*, as is Obstacle::Obstacle::Obstacle*, and so on. Pile on as many Obstacles you want and it's still just an Obstacle*.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
1

You cannot pass constructor pointer as stored type.

Instead of

std::vector<Obstacle::Obstacle*> obstacles;

try

std::vector<Obstacle*> obstacles;
Bartosz Przybylski
  • 1,628
  • 10
  • 15
  • Works! Will accept when it lets me in 8 minutes. I'm new to C++. I thought "Obstacle::" just set the namespace? Or is that just for functions...? – Isak T. Oct 14 '12 at 15:21
  • @PiotrNycz: the redundancy might well be the heart of the issue though. – Matthieu M. Oct 14 '12 at 15:29
  • @PiotrNycz I know there is no such thing, if you have better name for this I'll be happy to update it. – Bartosz Przybylski Oct 14 '12 at 15:33
  • [Here's](http://stackoverflow.com/questions/11423380/why-are-redundant-class-name-qualifiers-allowed) a question on some the oddities of using this `Foo::Foo` syntax to refer to a class. Different compilers handle it differently. – bames53 Oct 14 '12 at 16:14
  • @Bartek - see other answer. In C++ constructors are not ordinary functions/methods. You cannot take address of it. You cannot define type like address to constructor. Your advice is correct, but your explanation is wrong. This is just compiler inconsistency - `Obstacle` should be the same as `Obstacle::Obstacle` but this compiler does not want to treat this in this manner. That's all. – PiotrNycz Oct 14 '12 at 17:01
  • @PiotrNycz that's incorrect. `Obstacle` is not the same as `Obstacle::Obstacle`. The latter names the constructor (and is ill-formed in that context), and the former names the class. If you want to name the class with the latter kind-of-syntax, you can say `class Obstacle::Obstacle`. – Johannes Schaub - litb Oct 14 '12 at 17:45
  • @JohannesSchaub-litb - Just used incorrect "mental shortcut". I mean t this very context `vector` - vector definition `vector` - so `Obstacle::Obstacle` is used with `class`/`typename` in vector defined like in the question. Am I right or still wrong? – PiotrNycz Oct 14 '12 at 17:59
  • Huh? that's still just as incorrect as if you passed `int()` to a template non-type parameter of type `int`, because `int()` is a type. Whatever parameter you are passing to does not change the argument's interpretation here. – Johannes Schaub - litb Oct 14 '12 at 18:03