C ++ constexpr数组查找:内存开销?其他陷阱吗?

时间:2019-02-06 22:12:38

标签: c++

我已经基于this SO answer实现了constexpr map 数组查找,但是这让我想知道如果 map 会产生什么样的内存开销?数组非常大,使用此技术可能还会存在其他陷阱,尤其是在编译时无法解析constexpr函数的情况下。

这是一个人为的代码示例,希望可以使我的问题更加清楚:

example.h:

enum class MyEnum
{
  X0,
  X1,
  X2,
  X3,
  X4,
  X5
};

struct MyStruct
{
  const MyEnum type;
  const char* id;
  const char* name;
  const int size;
};

namespace
{
  constexpr MyStruct myMap[] = {
    {MyEnum::X0,"X0","Test 0", 0},
    {MyEnum::X1,"X1","Test 1", 1},
    {MyEnum::X2,"X2","Test 2", 2},
    {MyEnum::X3,"X3","Test 3", 3},
    {MyEnum::X4,"X4","Test 4", 4},
    {MyEnum::X5,"X5","Test 5", 5},
  };

  constexpr auto mapSize = sizeof myMap/sizeof myMap[0];
}

class invalid_map_exception : public std::exception {};

// Retrieves a struct based on the associated enum
inline constexpr MyStruct getStruct(MyEnum key, int range = mapSize) {
  return  (range == 0) ? (throw invalid_map_exception()):
          (myMap[range - 1].type == key) ? myMap[range - 1]:
          getStruct(key, range - 1);
};

example.cpp:

#include <iostream>
#include <vector>
#include "example.h"

int main()
{
  std::vector<MyEnum> enumList = {MyEnum::X0, MyEnum::X1, MyEnum::X2, MyEnum::X3, MyEnum::X4, MyEnum::X5};
  int idx;

  std::cout << "Enter a number between 0 and 5:" << std::endl;
  std::cin >> idx;

  MyStruct test = getStruct(enumList[idx]);

  std::cout << "choice name: " << test.name << std::endl;

  return 0;
}

输出:

Enter a number between 0 and 5:
1
choice name: Test 1

g++-std=c++14一起编译。

在上面的示例中,尽管getStruct是constexpr函数,但是直到运行时它才能完全解析,因为idx的值直到那时才知道。使用优化标志进行编译时,这是否可能会改变内存开销,还是将myMap的全部内容包含在二进制文件中?是否取决于所使用的编译器和优化设置?

此外,如果头文件包含在多个翻译单元中怎么办? myMap会重复吗?

我想如果 map 数组变得巨大和/或代码将在更多资源受限的环境(例如嵌入式设备)中使用,这可能很重要。

这种方法还有其他潜在的陷阱吗?

1 个答案:

答案 0 :(得分:2)

如果使用非常量表达式调用constexpr函数,它将在运行时调用该函数。

如果使用常量表达式调用getStruct,则编译器可以仅在编译时调用该函数。然后,getStruct函数将在运行时“不使用”,编译器可能会对其进行优化。此时,myMap也将不被使用并进行优化。

就运行时大小而言,它实际上可能小于std::unordered_mapstd::map;它实际上存储了必要的最少信息。但是它的查找时间会慢很多,因为它必须在O(N)时间中单独比较所有元素,因此它实际上并没有执行映射所要做的事情(减少了查找时间)。

如果您想使其更优化,我将确保仅在恒定表达的情况下使用它:

template<MyEnum key>
struct getStruct
{
    static constexpr const MyStruct value = _getStruct(key);
}

Here's some compiler output that shows that the map is optimised out entirely

关于将其包含在多个翻译单元中,由于您使用匿名命名空间对其进行定义,因此它将在每个翻译单元中重复。如果在所有这些代码中都对其进行了优化,则不会有开销,但是对于在运行时查找中使用的每个翻译单元,它仍然会重复。