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

时间:2022-09-10 15:09:14

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.

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

6 个解决方案

#1


63  

[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中被取消了,所以本地定义的类毕竟是有用的!感谢评论者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.

本地定义类的能力将使创建自定义函数(带有操作符()的类,例如传递给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 .1/2:一个本地类型,一个没有链接的类型,一个未命名的类型或任何类型复合的类型都不能作为模板类型参数的模板参数。

#2


26  

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.

虽然匿名命名空间也可以这样做。

#3


8  

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?

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

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兼容。所以留了下来。

#4


6  

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

#5


4  

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

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

#6


2  

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.

为了简单起见,本例使用了一个普通的非默认构造函数,以及在编译时已知值的情况。将这种技术扩展到需要用只在运行时才知道的值初始化对象数组的情况,是很简单的。

#1


63  

[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中被取消了,所以本地定义的类毕竟是有用的!感谢评论者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.

本地定义类的能力将使创建自定义函数(带有操作符()的类,例如传递给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 .1/2:一个本地类型,一个没有链接的类型,一个未命名的类型或任何类型复合的类型都不能作为模板类型参数的模板参数。

#2


26  

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.

虽然匿名命名空间也可以这样做。

#3


8  

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?

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

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兼容。所以留了下来。

#4


6  

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

#5


4  

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

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

#6


2  

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.

为了简单起见,本例使用了一个普通的非默认构造函数,以及在编译时已知值的情况。将这种技术扩展到需要用只在运行时才知道的值初始化对象数组的情况,是很简单的。

相关文章