简体   繁体   中英

Composing members at compile-time

I have a bunch of attributes which can be either NOP or have a state. The requirement for them is to not have any size when the user doesn't need the attribute, but still contain certain methods. An example:

struct AttributeATag {};

/* The template used when AttributeATag is not specified */
template <typename T>
class AttributeA
{
public:
    void foo(uint32_t v)
    {
        // Nop, do nothing
    }

    enum
    {
        HasAttributeA = false
    };
};

/* The template specialization used when AttributeATag is specified */
template <>
class AttributeA<AttributeATag>
{
public:
    void foo(uint32_t v)
    {
        this->omgVariable = v;
    }

    enum
    {
        HasAttributeA = true
    };
protected:
    int omgVariable;
};

template <typename ATag>
class MyUberClass : public AttributeA<ATag>
{
    // This class now has omgVariable or not, depending on ATag and it
    // has either a NOP method or one which actually does something
    void doSomething()
    {
        if (AttributeA<ATag>::HasAttributeA)
        {
            /* ... */
        }
    }
};

This works but now there is a problem: The size of NOP attributes, while being empty classes, is not 0, which means that 100 empty attributes add a lot of unused space to MyUberClass.

Is there a way to avoid that and add/remove member variables based on a template parameter?


EDIT:

As far as I know, empty classes do not have a size of 0. When I try the following, I get sizeof(B) == 4.

template <typename T>
class A
{

};

class B : public A<int>, public A<double>, public A<char>, public A<long>, public A<bool>
{

};

in this test:

#include <iostream>
struct g{};
int main()
{
    std::cout << sizeof(g) << std::endl;
}

on gcc I get a size of 1, this is probably because you still need to be able to have a pointer to it regardless as to whether or not it holds state.

I do not thin there is away around it without resorting to something other than a class.

Since you're using AttributeA as a base class, pretty much every compiler will use the "empty base optimization" to make sure that an empty base class uses no space within a child class, even though the size of the base class is nonzero. I don't believe you have a problem here.

Every class (bases/children) has to take at least one byte (and possibly four if your compiler pads everything), but empty bases (in almost every case) don't increase the size of child classes beyond what they would have been already.

An empty class would have minimum size of 1 byte, irrespective of any number of (empty) classes are inherited or not.
In your example, class B will have minimum implementation defined size, (which seems 4 bytes in your case), whether you inherit from other classes or don't inherit.

This is needed so that any empty class object can have a unique address. There is nothing much can be done about it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM