为什么我可以在 C++ 中的函数中定义结构和类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/876048/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:44:42  来源:igfitidea点击:

Why can I define structures and classes within a function in C++?

c++data-structuresfunctional-programming

提问by Robert Gould

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

我只是错误地在 C++ 中做了这样的事情,并且它有效。为什么我可以这样做?

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.

现在这样做之后,我有点记得很久以前在某个地方读到过这个技巧,作为 C++ 的一种穷人的函数式编程工具,但我不记得为什么这是有效的,或者我在哪里读过它。

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.

注意:虽然在编写问题时我没有得到任何对这个问题的引用,但当前的侧边栏指出了它,所以我将它放在这里以供参考,无论哪种方式,问题都是不同的,但可能有用。

回答by j_random_hacker

[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.

[编辑 18/4/2013]:令人高兴的是,下面提到的限制已在 C++11 中取消,因此本地定义的类毕竟有用!感谢评论者bambons。

The ability to define classes locally wouldmake 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.

定义类本地的能力让创建自定义函子(带类operator()(),如比较函数传递到std::sort()与其一起使用或“循环体” std::for_each()),就方便多了。

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. :(

不幸的是,C++ 禁止在模板中使用本地定义的类,因为它们没有链接。由于函子的大多数应用程序都涉及以函子类型为模板的模板类型,因此不能为此使用本地定义的类——您必须在函数外部定义它们。:(

[EDIT 1/11/2009]

[编辑 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.

14.3.1/2:.一个局部类型、一个没有链接的类型、一个未命名的类型或从这些类型中的任何一个复合的类型不得用作模板类型参数的模板参数。

回答by Nikolai Fetissov

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

本地定义的 C++ 类的一种应用是工厂设计模式


// 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.

虽然你可以对匿名命名空间做同样的事情。

回答by Charlie Martin

Well, basically, why not? A structin 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?

嗯,基本上,为什么不呢?A structin C(回到时间的黎明)只是声明记录结构的一种方式。如果你想要一个,为什么不能在你声明一个简单变量的地方声明它呢?

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

一旦你这样做了,请记住 C++ 的目标是尽可能与 C 兼容。所以就留了下来。

回答by simong

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.

它实际上对于做一些基于堆栈的异常安全工作非常有用。或者从具有多个返回点的函数中进行一般清理。这通常称为 RAII(资源获取即初始化)习语。

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.
}

回答by ChrisW

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

例如,它在http://www.icce.rug.nl/documents/cplusplus/cplusplus07.html 的“7.8:本地类:函数内部的类”部分中提到,该部分称其为“本地类”并说它“在涉及继承或模板的高级应用程序中非常有用”。

回答by Thomas L Holaday

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:

我有一个没有默认构造函数的类 C。我想要一个类 C 的对象数组。我弄清楚我希望如何初始化这些对象,然后使用静态方法从 C 派生一个类 D,该方法为 D 的默认构造函数中的 C 提供参数:

#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.

为了简单起见,这个例子使用了一个简单的非默认构造函数和一个值在编译时已知的情况。将此技术扩展到您希望使用仅在运行时已知的值初始化的对象数组的情况很简单。