113

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.

1

6 Answers 6

81

[EDIT 18/4/2013]: Happily, the restriction mentioned below has been lifted in C++11, so locally defined classes are useful after all! Thanks to commenter bamboon.

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Although empirically, this seems to work with MSVC++8. (But not with g++.)
Im using gcc 4.3.3, and it seems to work there: 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: 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.
This restriction was lifted in C++11.
35

One application of locally-defined C++ classes is in Factory design pattern:


// In some header
class Base
{
public:
    virtual ~Base() {}
    virtual void DoStuff() = 0;
};

Base* CreateBase( const Param& );

// in some .cpp file
Base* CreateBase( const Params& p )
{
    struct Impl: Base
    {
        virtual void DoStuff() { ... }
    };

    ...
    return new Impl;
}

Though you can do the same with anonymous namespace.

4 Comments

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.
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 :)
(I'm talking about the chicks BTW, not the answer!)
lol Robert... Yeah, nothing quite impresses a woman like knowing about obscure corners of C++...
18

It's actually very useful for doing some stack-based exception-safety work. Or general cleanup from a function with multiple return points. This is often called the RAII (resource acquisition is initialzation) idiom.

void function()
{

    struct Cleaner
    {
        Cleaner()
        {
            // do some initialization code in here
            // maybe start some transaction, or acquire a mutex or something
        }

        ~Cleaner()
        {
             // do the associated cleanup
             // (commit your transaction, release your mutex, etc.)
        }
    };

    Cleaner cleaner;

    // Now do something really dangerous
    // But you know that even in the case of an uncaught exception, 
    // ~Cleaner will be called.

    // Or alternatively, write some ill-advised code with multiple return points here.
    // No matter where you return from the function ~Cleaner will be called.
}

4 Comments

Cleaner cleaner(); I think this will be function declaration rather than a object definition.
@user You are correct. To call the default constructor, he should write Cleaner cleaner; or Cleaner cleaner{};.
Classes inside functions have nothing to do with RAII and, besides, this is not a valid C++ code and will not compile.
Even inside functions, classes like this are PRECISELY what RAII in C++ is all about.
12

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.

3 Comments

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++ :/
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.
Yes, I've migrated an existing C codebase to C++ (but not to Eiffel).
7

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

Comments

3

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;

  cout << "sizodf(C): " << sizeof(C) << endl;
  cout << "sizeof(D): " << sizeof(D) << 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.

2 Comments

Certainly an interesting application! Not sure it's wise or even safe though -- if you need to treat that array of D as an array of C (e.g. you need to pass it to a function taking a D* parameter) then this will silently break if D is actually larger than C. (I think...)
+j_random_hacker, sizeof(D) == sizeof(C). I added a sizeof() report for you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.