使用模板生成静态查找表
Using template to generate a static lookup table
我有:
const char kLetters[] = "QWERTYUIOPASDFGHJKLZXCVBNM";
我可以调用kLetters[n]
在O(1)时间内获得键盘字母表的第n个字母。然而,我将不得不迭代kLetter(花费O(n)或至少O(log n))时间进行反向查找。
我想使用模板创建一个反向查找表作为编译时静态查找表,并且想知道是否有一种方法可以做到这一点。
EDIT -正如评论中提到的,反向查找意味着我提供'E'并返回2。我的字母例子也不是最好的例子,我不想对顺序做任何假设。因此,我将字母改为键盘顺序。
这样怎么样?它允许您指定范围,而不是一个完整的字符串。
#include <iostream>
template <int Start, int End, int N>
struct lookup {
static_assert(Start != End, "Can't have 0 length lookup table");
enum { value = lookup<Start+(Start < End ? 1:-1),End,N-1>::value };
};
template <int Start, int End>
struct lookup<Start,End,0> {
enum { value = Start };
};
template <int Start, int End, int V, int P=0>
struct reverse_lookup {
static_assert(Start != End, "V isn't in the range Start, End");
static_assert(Start != End || !P, "Can't have 0 length range");
enum { value = reverse_lookup<Start+(Start < End ? 1:-1),End,V,P+1>::value };
};
template <int Start, int End, int P>
struct reverse_lookup<Start,End,Start,P> {
enum { value = P };
};
int main() {
std::cout << char(lookup<'A', 'Z', 3>::value) << std::endl;
std::cout << char(lookup<'Z', 'A', 3>::value) << std::endl;
std::cout << int(reverse_lookup<'A','Z','F'>::value) << std::endl;
}
好了,知道反向查找是什么之后,我想你可以这样做:
const char kLetters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int get_index(char letter)
{
return letter - 'A';
}
毕竟,字母A
在索引0
上,B
在索引1
上,C
在索引2
上……等等......这就足够了。
My O(1)解
. 到目前为止,其他解决方案适用于非任意字母序列,@awoodland解决方案假设要获取其索引的字母在编译时是已知的,这使得它不太有用。但是这个解决方案试图解决这两个限制;也就是说,它应该工作:
任意字母序列,如
const char Letters[] = "ZBADCEWFVGHIUXJTKSLYQMROPN";
这些字母在编译时可能是unknown。获取索引的函数具有以下签名:
int Index(char letter);
下面是使用@ David Rodríguez在他的博客中描述的技术的完整代码:
#include <iostream>
const char Letters[] = "ZBADCEWFVGHIUXJTKSLYQMROPN";
template<char L> int Index();
template<> int Index<'Z'>() { return 0; }
template<> int Index<'B'>() { return 1; }
template<> int Index<'A'>() { return 2; }
template<> int Index<'D'>() { return 3; }
template<> int Index<'C'>() { return 4; }
template<> int Index<'E'>() { return 5; }
template<> int Index<'W'>() { return 6; }
template<> int Index<'F'>() { return 7; }
template<> int Index<'V'>() { return 8; }
template<> int Index<'G'>() { return 9; }
template<> int Index<'H'>() { return 10; }
template<> int Index<'I'>() { return 11; }
template<> int Index<'U'>() { return 12; }
template<> int Index<'X'>() { return 13; }
template<> int Index<'J'>() { return 14; }
template<> int Index<'T'>() { return 15; }
template<> int Index<'K'>() { return 16; }
template<> int Index<'S'>() { return 17; }
template<> int Index<'L'>() { return 18; }
template<> int Index<'Y'>() { return 19; }
template<> int Index<'Q'>() { return 20; }
template<> int Index<'M'>() { return 21; }
template<> int Index<'R'>() { return 22; }
template<> int Index<'O'>() { return 23; }
template<> int Index<'P'>() { return 24; }
template<> int Index<'N'>() { return 25; }
typedef int (*fptr)();
const int limit = 26;
fptr indexLookup[ limit ];
template <char L>
struct init_indexLookup {
static void init( fptr *indexLookup ) {
indexLookup[ L - 'A' ] = &Index<L>;
init_indexLookup<L-1>::init( indexLookup );
}
};
template <>
struct init_indexLookup<'A'> {
static void init( fptr *indexLookup ) {
indexLookup[ 0 ] = &Index<'A'>;
}
};
const int ignore = (init_indexLookup<'Z'>::init(indexLookup),0);
int Index(char letter)
{
return indexLookup[letter-'A']();
}
下面是测试代码:
int main()
{
std::cout << Index('A') << std::endl;
std::cout << Index('Z') << std::endl;
std::cout << Index('B') << std::endl;
std::cout << Index('K') << std::endl;
}
输出:2
0
1
16
在线演示:http://ideone.com/uzE2t
嗯,这实际上是两个函数调用:一个到Index()
,另一个到indexLookup
中的一个。你可以通过写(ideone):
int main()
{
std::cout << indexLookup['A'-'A']() << std::endl;
std::cout << indexLookup['Z'-'A']() << std::endl;
std::cout << indexLookup['B'-'A']() << std::endl;
std::cout << indexLookup['K'-'A']() << std::endl;
}
这看起来很麻烦,但是嘿,我们可以让Index()
inline:
inline int Index(char letter)
{
return indexLookup[letter-'A']();
}
看起来很好,现在编译器很可能会把它等同于一个函数调用!
简单的O(1)解
等。我刚刚意识到整个解决方案简化为一个初始化为:
的查找表。 const int indexLookup[] = {2,1,4,3,5,7,9,10,11,14,16,18,21,
25,23,24,20,22,17,15,12,8,6,13,19,0};
inline int Index(char letter)
{
return indexLookup[letter-'A'];
}
看起来非常简单!
如果您可以使用Boost并且只需要编译时查找:
using namespace boost::mpl;
typedef vector_c<char, 'A', 'B', 'C', 'D'> Chars;
// lookup by index:
std::cout << at_c<Chars, 1>::type::value << std::endl; // B
// lookup by value:
typedef find<Chars, integral_c<char, 'C'> >::type Iter;
std::cout << Iter::pos::value << std::endl; // 2
假设'Z'> 'A',但不假设字母是连续的。(虽然它占用更少的内存)我很想放入if (numrLetters>26)
条件,这样智能编译器就可以使用加法而不是ASCII表,但后来决定我不想在不那么智能的编译器的情况下减慢代码。
const char kLetters[] = "ABCDEFGHJJKLMNOPQRSTUVWXYZ";
const int numLetters = sizeof(kLetters);
const char rkLetters['Z'-'A'] = {};
const int numrLetters = sizeof(rkLetters);
struct LetterInit {
LetterInit() {
for(int i=0; i<numLetters; ++i)
rkLetters[kLetters[i]-'A'] = i;
}
}LetterInitInst;
char findChar(int index) {
assert(index>=0 && index<numLetters);
return kLetters[index];
}
int findIndex(char letter) {
assert(letter>='A' && letter<='Z');
return rkLetters[letter-'A'];
}
由于有几种不生成表但仍允许编译时查找的解决方案,这里是另一个
constexpr char kLetters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
constexpr int get(char const x, int const i = 0) {
return kLetters[i] == x ? i : get(x, i + 1);
}
在编译时使用
int x[get('F')];
static_assert(sizeof(x) == sizeof(int[5]), "");
指定一个不存在的字符将导致错误。如果你在运行时使用这个函数,如果你指定了一个不存在的字符,你会得到未定义的行为。可以对这些情况进行适当的检查。
生成找到的第一个字符的索引。如果一个字符在haystack中出现两次,则不会给出错误。
如果您可以使用c++0x(使用gcc 4.5进行测试),则可以:
#include<initializer_list>
#include<iostream>
#include<map>
constexpr int getLetterNumber(char a){ return std::map<char,int>({{'a',2},{'b',1},{'c',4}})[a]; }
int main(){
const char ch='b';
std::cout<<ch<<": "<<getLetterNumber(ch)<<std::endl;
}
constexpr
强制编译时求值。
EDIT:这个解决方案是不正确的,正如所指出的。constexpr
不强制编译时求值。这确实在编译时进行查找(类似于同时发布的解决方案)。
#include<iostream>
template<char C> int ch2Num();
#define CHR(c,i) template<> int ch2Num<c>(){ return i; }
CHR('a',2); CHR('b',1); /* ... */
#undef CHR
int main(void){
const char ch='b';
std::cout<<ch<<": "<<ch2Num<ch>()<<std::endl;
};
- 正在查找文档以获得PS4平台的C++中的设备信息
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 在C++中查找文件
- #为""定义宏;静态";针对不同的上下文
- 使用gcc从静态链接的文件中查找可选符号
- 在链接可执行文件之前查找静态库未解析的依赖项
- Cmake 查找模块用于区分共享库或静态库
- 动态链接到 c++ 静态成员字段时符号查找失败
- C++-如何制作查找矩阵的静态字典
- 如何查找全局静态初始化
- 映射查找函数调用 Vs 将查找缓存为静态变量
- 在 Linux 库中查找静态函数地址
- xlC模板函数问题的静态函数查找
- VS2010查找调试符号时,静态库链接
- 查找指向对象的静态指针
- 使用模板生成静态查找表
- 什么名称查找规则适用于静态 const 数据成员定义中的名称
- 在c++中创建并初始化一个静态const char数组作为Ascii查找表
- 如何查找函数在编译时是否为静态函数
- C++:在初始值设定项中类静态变量的定义中查找名称