代码之家  ›  专栏  ›  技术社区  ›  0liveradam8

编写仅因结构/类成员变量名不同的类似函数的更好方法?

  •  1
  • 0liveradam8  · 技术社区  · 1 年前

    我定义了一个 struct enum It’是这样的:

    enum class ETerrainType : uint16_t {/*Enum types here*/};
    enum class EBuildingType : uint16_t {/*Enum types here*/};
    enum class EDecorationType : uint16_t {/*Enum types here*/};
    
    struct Tile
    {
        ETerrainType TerrainType;
        EBuildingType BuildingType;
        EDecorationType DecorationType;
    };
    

    请注意,结构的成员变量都继承了相同的类型( uint16_t )。

    我有一个装瓷砖的容器 std::vector<Tile> Tiles;
    我为每个成员变量创建一个包含循环的函数,如下所示:

    void TerrainTypeFunc()
    {
        for (Tile& tile : Tiles)
        {
            DoSomething(static_cast<uint16_t>(tile.TerrainType));
        }
    }
    
    void BuildingTypeFunc()
    {
        for (Tile& tile : Tiles)
        {
            DoSomething(static_cast<uint16_t>(tile.BuildingType));
        }
    }
    
    void DecorationTypeFunc()
    {
        for (Tile& tile : Tiles)
        {
            DoSomething(static_cast<uint16_t>(tile.DecorationType));
        }
    }
    

    在我看来,当每个函数之间的唯一区别是被访问的成员时,重新定义每个函数是一种糟糕的做法。

    我的第一个想法是将成员变量存储为的固定数组 uint16_t s 然而 ,我想利用结构打包(例如,编译器可以将这三个成员变量存储在一个64位字中)。
    据我所知,以这种方式储存是不允许包装的。另外,我认为按名称使用成员变量比按索引使用成员变量更易读。

    另一种选择是使用偏移量来访问变量;例如,获取指向的指针 TerrainType 并将1添加到指针以获得 BuildingType ,或将2添加到指针以获得 DecorationType
    据我所知,这是一种糟糕的做法,因为很难确定使用什么偏移量,因为在不同的情况下可能存在或不存在结构封装。另外,我认为这对可读性不好。

    第三种选择是使用一个函数,该函数通过分支确定在运行时使用哪个参数。我宁愿不这样做,因为我不想产生任何运行时成本。

    我的问题是:
    如何在不产生额外运行时成本的情况下,使用更少的代码定义这三个函数?
    我认为最好可以使用模板来完成,如果不能的话,可以使用宏,但我不知道怎么做。

    2 回复  |  直到 1 年前
        1
  •  2
  •   zdan    1 年前

    您可以使用 std::invoke 函数,将每个数据成员视为传递到公共函数中的可调用对象,如下所示:

    template<typename F>
    void DoSomethingOn(F typeLookup)
    {
        for (Tile& tile : Tiles)
        {
            DoSomething(static_cast<uint16_t>(std::invoke(typeLookup ,tile )));
        }
    }
    

    你可以这样称呼它:

    doSomethingOn(&Tile::TerrainType);
    doSomethingOn(&Tile::BuildingType);
    doSomethingOn(&Tile::DecorationType);
    
        2
  •  0
  •   TooZni    1 年前

    可以使用完全专业化来获取与参数化类型相对应的成员。

    struct Tile {
        //...
    
        template<>
        inline ETerrainType memberOfType<ETerrainType>() const
        {
            return TerrainType;
        }
    
        template<>
        inline EBuildingType memberOfType<EBuildingType>() const
        {
            return BuildingType;
        }
    
        template<>
        inline EDecorationType memberOfType<EDecorationType>() const
        {
            return DecorationType;
        }
    }
    

    或者在C++20中,您可以使用 if constexpr

    struct Tile {
        //...
    
        template<typename T>
        inline T memberOfType() const {
            if constexpr (std::is_same_v<T, ETerrainType>) {
                return TerrainType;
            }
            else if constexpr (std::is_same_v<T, EBuildingType>) {
                return BuildingType;
            }
            else {
                return DecorationType;
            }
        }
    }
    

    两者都可以与bog标准函数模板一起使用:

    template <typename T>
    void Func() {
        // ...
        DoSomething(static_cast<uint16_t>(tile.memberOfType<T>()));
    }
    

    请记住,如果您不小心打电话给 tile.memberOfType<FooType> 或者你会得到一个奇怪的错误 EDecorationType 不可隐式转换为 FooType ,而不仅仅是被告知的专业化 FooType 不存在。尽管这可以通过一个只接受所需类型的概念来解决。