views:

970

answers:

5

I just mistakenly did something like this in C++, and it works. Why can I do this?

int main(int argc, char** argv) {
    struct MyStruct
    {
      int somevalue;
    };

    MyStruct s;
    s.somevalue = 5;
}

Now after doing this, I kind of remembered reading about this trick someplace, a long time ago, as a kind of poor-man's functional programming tool for C++, but I can't remember why this is valid, or where I read it.

Answers to either question are welcome!

Note: Although when writing the question I didn't get any references to this question, the current side-bar points it out so I'll put it here for reference, either way the question is different but might be useful.

+3  A: 

Well, basically, why not? A struct in C (going back to the dawn of time) was just a way to declare a record structure. If you want one, why not be able to declare it where you would declare a simple variable?

Once you do that, then remember that a goal of C++ was to be compatible with C if at all possible. So it stayed.

Charlie Martin
kind of an neat feature to have survived, but as j_random_hacker just pointed out it's not as useful as I was imagining in C++ :/
Robert Gould
Yeah, the scoping rules were weird in C too. I think, now that I've 25+ years experience with C++, that maybe striving to be as much like C as they did might have been a mistake. On the other hand, more elegant languages like Eiffel weren't adopted nearly as readily.
Charlie Martin
Yes, I've migrated an existing C codebase to C++ (but not to Eiffel).
ChrisW
+2  A: 

It's mentioned at, for example, section 6.5 of http://www.icce.rug.nl/documents/cplusplus/cplusplus06.html which calls it a "local class" and says it "can be very useful in advanced applications involving inheritance or templates".

ChrisW
thanks for the link!
Robert Gould
+5  A: 

The ability to define classes locally would make creating custom functors (classes with an operator()(), e.g. comparison functions for passing to std::sort() or "loop bodies" to be used with std::for_each()) much more convenient.

Unfortunately, C++ forbids using locally-defined classes with templates, as they have no linkage. Since most applications of functors involve template types that are templated on the functor type, locally defined classes can't be used for this -- you must define them outside the function. :(

[EDIT 1/11/2009]

The relevant quote from the standard is:

14.3.1/2: .A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter.

j_random_hacker
that explains why I couldn't get that to work just now
Robert Gould
Although empirically, this seems to work with MSVC++8. (But not with g++.)
j_random_hacker
could be a side effect of lambda support, just a wild guess thou...
Robert Gould
Im using gcc 4.3.3, and it seems to work there: http://pastebin.com/f65b876b. Do you have a reference to where the standard forbids it? It seems to me that it could easily be instantiated at the time of use.
Catskul
@Catskul: 14.3.1/2: "A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter". I guess the rationale is that local classes would require yet another bunch of information to be pushed into mangled names, but I don't know that for sure. Of course a particular compiler may offer extensions to get around this, as it seems MSVC++8 and recent versions of g++ do.
j_random_hacker
+4  A: 
Nikolai N Fetissov
Interesting! Although the restrictions regarding templates I mention will apply, this approach guarantees that instances of Impl can't be created (or even talked about!) except by CreateBase(). So this seems like an excellent way to reduce the extent to which clients depend on implementation details. +1.
j_random_hacker
That's a neat idea, not sure if I'll be using it anytime soon, but probably a good one to pull out at the bar to impress some chicks :)
Robert Gould
I'll give you all of my SO points if that works...
markh44
(I'm talking about the chicks BTW, not the answer!)
markh44
lol Robert... Yeah, nothing quite impresses a woman like knowing about obscure corners of C++...
j_random_hacker
+2  A: 

It's for making arrays of objects that are properly initialized.

I have a class C which has no default constructor. I want an array of objects of class C. I figure out how I want those objects initialized, then derive a class D from C with a static method which provides the argument for the C in D's default constructor:

#include <iostream>
using namespace std;

class C {
public:
  C(int x) : mData(x)  {}
  int method() { return mData; }
  // ...
private:
  int mData;
};

void f() {

  // Here I am in f.  I need an array of 50 C objects starting with C(22)

  class D : public C {
  public:
    D() : C(D::clicker()) {}
  private:
    // I want my C objects to be initialized with consecutive
    // integers, starting at 22.
    static int clicker() { 
      static int current = 22;
      return current++;
    } 
  };

  D array[50] ;

  // Now I will display the object in position 11 to verify it got initialized
  // with the right value.  

  cout << "This should be 33: --> " << array[11].method() << endl;

  return;

}

int main(int, char **) {
  f();
  return 0;
}

For the sake of simplicity, this example uses a trivial non-default constructor and a case where the values are known at compile time. It is straightforward to extend this technique to cases where you want an array of objects initialized with values that are known only at runtime.

Thomas L Holaday