首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在C++编译时设计具有可切换成员变量的模板类?

如何在C++编译时设计具有可切换成员变量的模板类?
EN

Stack Overflow用户
提问于 2020-03-18 03:17:42
回答 2查看 266关注 0票数 0

definition____________________

我试图设计一个超级灵活,但内存高效的模块满足以下属性.

  1. 它可以根据情况关闭不必要的成员变量。
  2. 它将拥有哪些变量是在编译时确定的.

我以某种方式做出了一个确定其成员列表"before“的编译时,使用宏和枚举器标志。见下文:

TraitSwitch.h

代码语言:javascript
复制
#pragma once
// Macros to switch-off source codes themselves.
#define ON                 1
#define OFF                0

#define TRI_AREA_INFO      ON
#define TRI_CENTROID_INFO  ON
#define TRI_NORMAL_INFO    OFF // When the normal vector info is unnecessary.
...

TriangleTraits.h

代码语言:javascript
复制
#pragma once
#include <cstdint>
#include "TraitSwitch.h"

enum TriangleTrait : uint8_t
{
    NONE          = 0,   // 0000 0000

#if (TRI_AREA_INFO == ON)
    AREA          = 1,   // 0000 0001
#endif

#if (TRI_CENTROID_INFO == ON)
    CENTROID      = 2,   // 0000 0010
#endif

#if (TRI_NORMAL_INFO == ON) //        | Inactive Preprocessor Block
    NORMAL_VECTOR = 4,   // 0000 0100 |
#endif
    ... // more traits

    ALL           = 255  // 1111 1111
}
// Need some additional overloaded bitwise-operators (&, |, |=, etc ...)

Triangle.h

代码语言:javascript
复制
#pragma once
#include "TriangleTraits.h"

class Triangle
{
public:
    Triangle() {}
    ~Triangle() {}

#if (TRI_AREA_INFO == ON)
    double area;
#endif

#if (TRI_CENTROID_INFO == ON)
    double centroid[3]; // x, y, z
#endif

#if (TRI_NORMAL_INFO == ON) //   | Inactive Preprocessor Block
    double normal[3]; // x, y, z |
#endif
    ...

    TriangleTrait alreadyComputed; // To avoid redundant works.
    void ComputeTraits(TriangleTrait _requested)
    {
        if (((_requested & TriangleTrait::AREA) != 0) 
            && ((alreadyComputed & _requested) == 0))
        {
            this->ComputeArea();
            alreadyComputed |= TriangleTrait::AREA;
        }
        ... // do the same things for centroid, normal
    }

private:
    void ComputeArea();
    void ComputeCentroid();
    void ComputeNormal();
    ...
}

然后,对象上的C++ IntelliSense可能会显示:

main.cpp

代码语言:javascript
复制
#include <iostream>
#include "Triangle.h"

int main(void)
{
    Triangle tri;
    tri.ComputeTraits(TriangleTrait::AREA | TriangleTrait::CENTROID);

    std::cout << "area : " << tri.area << "m²" << std::endl;
    std::cout << "centroid : (" 
        << tri.centroid[0] << "," 
        << tri.centroid[1] << "," 
        << tri.centroid[2] << ")" << std::endl;
    ...
}

首先,"before" Triangle.h看起来很难看,即使它看起来不错,这个方法还是确定了类成员编译时

代码语言:javascript
复制

=问题summary____________________

“如何使用可切换成员__设计模板类,这些成员在编译时__中确定为。”

我最想要的就是:

main.cpp

代码语言:javascript
复制
...

int main(void)
{
    Triangle<__MACRO_DEFINED_TRAIT_SWITCH(AREA)> tri1; // This owns area info only
    tri1.area;

    Triangle<__MACRO_DEFINED_TRAIT_SWITCH(AREA | CENTROID)> tri2; // This owns area & centroid info
    tri2.area;
    tri2.centroid;

    Triangle<__MACRO_DEFINED_TRAIT_SWITCH(AREA | NORMAL)> tri3; // This owns area & normal vector info
    tri3.area;
    tri3.normal;
    ...

    Triangle<__MACRO_DEFINED_TRAIT_SWITCH(AREA | CENTROID | NORMAL)> tri4; // This owns area & centroid & normal vector info
    tri4.area;
    tri4.centroid;
    tri4.normal;
    ...
}

我猜使用模板与宏相结合(可能是使用标记分派方法?)会做我想做的事,但没有任何明确的想法。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-03-20 03:36:46

经历了几天的痛苦之后,我找到了一条路。所以我自己回答我的问题。

这个想法太简单了。只需将多项继承与可变模板结合使用即可。见下文:

▼TriangleTraits.h

代码语言:javascript
复制
struct AREA
{
    double area;
};

struct CENTROID
{
    double centroid[3];
};

struct NORMAL
{
    double normal[3];
};
... // more traits

// Multiple Inheritances with variadic template
template<class... Traits>
struct TriangleWithTraits : Traits... 
{
};

main.cpp

代码语言:javascript
复制
#include "TriangleTraits.h" // Just include this one is enough

int main()
{
    TriangleWithTraits<AREA> tri1;
    tri1.area;

    TriangleWithTraits<AREA, CENTROID> tri2;
    tri2.area;
    tri2.centroid;

    TriangleWithTraits<AREA, NORMAL> tri3;
    tri3.area;
    tri3.normal;

    TriangleWithTraits<AREA, CENTROID, NORMAL> tri4;
    tri4.area;
    tri4.normal;
    tri4.centroid;
...
}
票数 2
EN

Stack Overflow用户

发布于 2020-03-19 04:06:04

不能使用预处理器解析模板参数,预处理器不知道C++。

但是有std::conditional和空的基优化。

因此,您可以使用数据类部件和std::conditional在它们和空基之间进行选择:

代码语言:javascript
复制
struct EmptyBase {};

struct BaseWithChar
{
    char a;
};

struct BaseWithLongLong
{
    long long b;
    long long c;
};

template<int Flags>
struct A
    : std::conditional<(Flags & 1), BaseWithChar, EmptyBase>::type
    , std::conditional<(Flags & 2), BaseWithLongLong, EmptyBase>::type
{
};

int main() {
    std::cout << sizeof(A<1>) << std::endl;
    std::cout << sizeof(A<2>) << std::endl;
    std::cout << sizeof(A<3>) << std::endl;
    return 0;
}

在后来的C++标准中,有std::coditional_t[[no_unique_address]]

代码语言:javascript
复制
using namespace std;

struct EmptyMember {};

template<int Flags>
struct A
{
    [[no_unique_address]] typename std::conditional_t<(Flags & 1), char, EmptyMember> a;
    [[no_unique_address]] typename std::conditional_t<(Flags & 2), long long, EmptyMember> b;
    [[no_unique_address]] typename std::conditional_t<(Flags & 2), long long, EmptyMember> c;
};

int main() {
    std::cout << sizeof(A<1>) << std::endl;
    std::cout << sizeof(A<2>) << std::endl;
    std::cout << sizeof(A<3>) << std::endl;
    return 0;
}

但是[[no_unique_address]]需要C++20,如果没有[[no_unique_address]],空成员将占用一些空间。看起来它不适用于VisualC++,甚至是2019年预览版。我发了菲巴达克

使用早期的C++,当您没有std::conditionalboost::conditional时,您可以很容易地实现自己的,它只是一个具有真和假专门化的模板。参见偏好页上的“可能实现”

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60733043

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档