语法中的哪一点是声明的类?

时间:2017-08-05 10:22:22

标签: c++ class

例如,以下是否会在运行时导致任何错误?

    template<typename Ty>
    struct Foo {

    };
                        // would this work during run time?
    struct Bar : public Foo<Bar> {

    };

如果确实有效,这是不好的做法吗?它没有被优化吗?

4 个答案:

答案 0 :(得分:2)

Bar is declared at the point of struct Bar appearing. It is an incomplete type until the class definition is complete.

Your code is legal and is in fact a known design pattern called Curiously recurring template.

答案 1 :(得分:1)

Your code doesn't cause any errors. Its called as curiously recurring template pattern.

It is used to create a specialised base class definition for each of the sub class instead of a common base class definition.

Consider the example:

template<class T>
class Base
{
    static int counter;
public:
    Base()
    {
        ++ counter;
    }

    ~Base()
    {
        -- counter;
    }

    static int getCounter()
    {
        return counter;
    }
};

template<class T>
int Base<T>::counter = 0;

class A : public Base<A>
{
};

class B : public Base<B>
{
};

int main()
{
   A a1, a2, a3;
   B b1, b2;

   cout << "A count is : " << A::getCounter() << endl;
   cout << "B count is : " << B::getCounter() << endl;
}

Without curiously recurring template pattern, the output would be 5 and 5 for both classes (A & B)

With the said pattern applied, the output will be 3 and 2 for the A & B classes respectively.

Hope this helps :-)

答案 2 :(得分:0)

Just like any other situation where you have a incomplete type (like when a class is forward declared), you cannot use the full object in the template. Logically it cannot be allowed because it would cause infinite recursion trying to populate a data type which includes itself.

template<typename Ty>
struct Foo {
    Ty   ty1; // <== Not allowed, incomplete type
    Ty * ty2; // <== Just fine
    Ty & ty3; // <== Just fine
};

struct Bar : public Foo<Bar> {

};

Live: https://godbolt.org/g/sf72Wp

It's no different than a class which uses itself:

Class Foo {
    Foo * f;
};

答案 3 :(得分:0)

Here is what the OP asked:

For example, would the following cause any errors during runtime?

template<typename Ty>
struct Foo {};

// would this work during run time?
struct Bar : public Foo<Bar> {};

If it does work, is it bad practice? Is it unoptimized?


What is shown in the question is that there is an empty struct Foo that is of a template type. The only thing this struct has is a default ctor & dtor, there is no consumption of stack memory outside of the default ctor & dtor for there are no members. This by itself will compile absolutely fine and will not cause any runtime error. The next line in question struct Bar : Foo<Bar>{} is an empty struct Bar that is not a tamplate type that derives from a template type that happens to be instantiated as a class template of type <Bar> and again Bar has the same operability as Foo because it has only a default ctor & dtor. So this would still not cause any problems at runtime.

Now as for the last question and its two parts. If it does work, is it bad practice? Is it unoptimized?

What do you mean by unoptimized?

If it does work, is it bad practice?

Well this is the more important question...

Why?

It doesn't matter in this context until you start adding components to the classes. As it stand as is with these declarations, there are no executions or instructions happening after and before the ctor & dtor calls. These as they are, are both its declaration & definition since there are no member variables or methods to perform any calculations, comparisons or anything else. There is no existing variable declaration of the class type being instantiated such as in this snippet.

template<typename T> class Foo{};
class Bar : public Foo<Bar> {};

int main () {
    Bar bar; // <-- now the default ctor is used and `bar` is now a declared variable 
             // in memory of type class Bar;

    // Next line of code here
    return 0; 
}  // -> default dtor is used

And this should still compile and run because of the fact that as these classes currently stand: bar.* or bar->* where * means any member is meaningless in this context since both classes here have a scope that is completely empty and there is nothing to do or perform.

Now when you start to add members of different types and methods to work on them then some things will work and others wont. For it to work Bar must be a complete type as you can not use an abstract class in this case. There might be a little more involved than this but this is as much as I can answer for this question. I hope this helps and if anyone sees any problems with this answer please leave a reply first as to what is wrong first and then let me address it before just down voting it right a way without giving a word. A negative comment can be more constructive than just a class Boo {};!