tags:

views:

508

answers:

7

I've decided to get more acquainted with my favorite programming language, but only reading the standard is boring.

What are the most surprising, counter-intuitive, or just plain weird elements of C++? What has shocked you enough that you ran to your nearest compiler to check if it's really true?

I'll accept the first answer that I won't believe even after I've tested it. :)

+6  A: 

The order of several declarators is actually unordered:

volatile long int const long extern unsigned x;

is the same as

extern const volatile unsigned long long int x;
280Z28
Is that really so surprising? Care to explain why you were surprised?
akent
I, for one, didn't know you could separate the "long long int" part.
Jurily
Actually, it is illegal for storage specifiers like "extern" to appear inside the type specifier. They must appear before or after. Your first example is not valid C++, but it would be if you swapped "extern" and "unsigned".
Shmoopty
@Shmoopty: based on the formal grammar rule alone, you are correct. However, section 7.1.6.2 (dcl.type.simple) point 3 states "When multiple *simple-type-specifiers* are allowed, they can be freely intermixed with other *decl-specifiers* in any order." This pulled from the C++0x draft here (not sure about the other standards): http://www.research.att.com/~bs/SC22-N-4411.pdf
280Z28
+4  A: 

Considering how unforgiving C++ usually is, I found it somewhat surprising that the standard actually allows you to delete null pointers.

Charles Salvia
I find it surprising how many people program in C++ that do not know that you can delete null pointers.
bk1e
I learned C before moving on to C++, so I learned not to do _anything_ with a NULL pointer. The fact that C++ defines a no-op for `delete NULL;` might be useful, but it can also hide bugs.
Jurily
No surprise there if you realize that in C `free(NULL)` is well-defined.
MSalters
+9  A: 

Another answer I could add would be the throw() qualifier. For example:

void dosomething() throw()
{
   // ....
}

void doSomethingElse() throw(std::exception)
{
  // ....
}

Intuitively, this seems like a contract with the compiler, indicating that this function is not allowed to throw any exceptions except for those listed. But in reality, this does nothing at compile time. Rather it is a run-time mechanism and it will not prevent the function from actually throwing an exception. Even worse, if an unlisted exception is thrown, it terminates your application with a call to std::terminate().

Charles Salvia
Herb Sutter explains in great detail why you should never write exception specifications: http://www.gotw.ca/publications/mill22.htm
James McNellis
That's exactly why we use the throw statement commented out, just for documentation purpose... quite a pity :/
Matthieu M.
+1  A: 

It is not very well known that an array initiator may jump in index like in the enumeration definition.

// initializing an array of int
int a[ 7] = { [5]=1, [2]=3, 2};
// resulting in
int a[ 7] = { 0, 0, 3, 2, 0, 1, 0};

// initializing an array of struct
struct { int x,y; } ar[ 4] = { [1].x=23, [3].y=34, [1].y=-1, [1].x=12};
// resulting in
struct { int x,y; } ar[ 4] = { { 0, 0}, { 12, -1}, { 0, 0}, { 0, 34}};

// interesting usage
char forbidden[ 256] = { ['a']=1, ['e']=1, ['i']=1, ['o']=1, ['u']=1};

Most of these are true for C++ also.

Lazer
-1, this is a gcc extension. The question mentions the standard according to which the above snippet is ill-formed.
avakar
yeah this is gcc specific. as awesome as gcc is, it's not part of the standard.
Matt Joiner
+6  A: 

I found it somewhat surprising that

class aclass
{
public:
int a;
};

some_function(aclass());

will have ainitialized to 0 in some_function, while

aclass ac;
some_function(ac);

will leave it unitinitalized. If you explicitly define the default constructor of aclass:

class aclass
{
public:
aclass(): a() {}
int a;
};

then aclass ac; will also initialize a to 0.

henle
what? care to explain why it differs? does it initialize stack space pushed for the sake of a call?
Matt Joiner
Using `aclass()` as the parameter makes the compiler fill out a default constructer that initializes everything to the default. But when you only declare a variable, no constructor is called unless you defined a default constructor yourself.
henle
hmm, it's true, i tried it out :P. +1 for something i didn't know.
Matt Joiner
+1  A: 

Objects change type during construction.

In particular, when calling a virtual function from a constructor, you will not be calling the most derived override. Rather, you are calling the implementation from the class you're currently constructing. If that implementation happens to be 0 (pure virtual function), your program will crash at runtime.

A semi-real-world example:

class AbstractBase {
  public:
    AbstractBase() {
      log << "Creating " << className() << endl;
    }
  protected:
    virtual string className() const = 0;
}

class ConcreteGuy {
  protected:
    virtual string className() const { return "ConcreteGuy"; }
}

Upon constructing a ConcreteGuy object, the program will terminate with a "calling pure virtual function" error message.

This is why calling virtual functions from constructors is considered evil.

Thomas
its not that surprising really, when you construct a ConcreteGuy (assuming it is derived from AbstractBase) as the base class gets constructed first, it doesn't have a ConcreteGuy class to call until the constructor is complete.
gbjbaanb
It does make sense, I agree. But it still caused quite some headscratching when I first ran into this. Especially since Java and C# behave differently, allowing you to call methods on objects that are not fully constructed.
Thomas
@gbjbaanb (what a name!): Not really, the compiler will match the construction call with the most derived type constructors and start executing that constructor. Now, the order of execution is first the initialization list, then the constructor body, and the standard states that the initialization starts executing the first base class of the current class. The algorithm entails that the first type that is fully constructed (both initialization list and constructor body competed) is the less derived type (by some definition of it)...
David Rodríguez - dribeas
... the fact is that the compiler knows the exact type of the most derived object beforehand. The fact that the virtual table gets initialized in the order it does is a design issue. Both Java and C# took a different approach, and while they will also construct from the base down to the most derived type, the virtual method table (or equivalent mechanism) is fully constructed before calling the base constructor with the most derived type methods. This, on the other hand is trading one problem for another, as a virtual method can be called on a not-yet-constructed level of the hierarchy.
David Rodríguez - dribeas
A: 

In C++ statements evaluate to something...

int main()
{
    "This is a valid C++ program!"
    "I will list the first five primes:";
    2;
    3;
    5;
    7;
    11;
}
AraK
It's not a valid program without a return statement.
Jurily
Actually, in C++, `main` need not explicitly return a value.
James McNellis
@Jurily It is! check the standards :)
AraK
@James McNellis: You could post this as an answer instead of a comment :)
Thomas