如何在由用于表示字面值的字符类型参数化的模板中表示字符串字面值?

11

考虑下面这个简单的地图:

class MyCoolMap : public unordered_map<const char *, const char *>
{
public:
  ProtoTypeMap()
  {
    insert(value_type("in1", "out1"));
    insert(value_type("in2", "out2"));
    ...
    insert(value_type("inN", "outN"));
  }
};

现在,假设我需要同时为charwchar_t字符串使用这个映射表。因此,我将其重写如下:

template<class C>
class MyCoolMap : public unordered_map<const C *, const C *>
{
public:
  MyCoolMap()
  {
    insert(value_type("in1", "out1"));
    insert(value_type("in2", "out2"));
    ...
    insert(value_type("inN", "outN"));
  }
};

当然,这对于 C=wchar_t 是不起作用的。问题在于我不知道如何模板化 char 字面量和 wchar_t 字面量之间的差异。现在我看到两个解决方案,都很丑陋。

解决方案1 - 通过 wchar_t 特化 MyCoolMap

template<>
class MyCoolMap<wchar_t> : public unordered_map<const wchar_t *, const wchar_t *>
{
public:
  MyCoolMap()
  {
    insert(value_type(L"in1", L"out1"));
    insert(value_type(L"in2", L"out2"));
    ...
    insert(value_type(L"inN", L"outN"));
  }
};

这样做是不好的,因为整个逻辑都被复制了。

解决方案2 - 像traits一样的解决方案:

#define _TOWSTRING(x) L##x
#define TOWSTRING(x) _TOWSTRING(x)

template <class C, int> struct special_string;
#define DECL_SPECIAL_STRING(STR) \
const int ss_##STR = __LINE__; \
template<> struct special_string<char, ss_##STR> { static const char *get_value() { return #STR; } }; \
template<> struct special_string<wchar_t, ss_##STR> { static const wchar_t *get_value() { return TOWSTRING(#STR); } };

DECL_SPECIAL_STRING(in1)
DECL_SPECIAL_STRING(out1)
DECL_SPECIAL_STRING(in2)
DECL_SPECIAL_STRING(out2)
...
DECL_SPECIAL_STRING(inN)
DECL_SPECIAL_STRING(outN)

template<class C>
class MyCoolMap : public unordered_map<const C *, const C *>
{
public:
  MyCoolMap()
  {
#define INSERT_MAPPING(in, out) insert(value_type(special_string<C, ss_##in>::get_value(), special_string<C, ss_##out>::get_value()))
    INSERT_MAPPING(in1, out1);
    INSERT_MAPPING(in2, out2);
    ...
    INSERT_MAPPING(inN, outN);
#undef INSERT_MAPPING
  }
};

这种方法不需要复制逻辑,但非常冗长,且 heavily relies on macros(宏)。

一定有更好的方法,我只是没看出来。

我正在使用 VS2010。

编辑

很高兴有一个更简单的解决方案被提出 - 鸣谢 https://stackoverflow.com/users/5987/mark-ransom。不过,我必须做一些小修改才能使其编译:

#define _TOWSTRING(x) L##x
#define TOWSTRING(x) _TOWSTRING(x)

template<typename C> const C * ChooseCW(const char * c, const wchar_t * w);
template<> const char * ChooseCW<char>(const char * c, const wchar_t * w)
{
  return c;
}
template<> const wchar_t *ChooseCW<wchar_t>(const char * c, const wchar_t * w)
{
  return w;
}

#define CW(C, STR) ChooseCW<C>(#STR, TOWSTRING(#STR))
再次感谢。

2
你的问题在于,虽然这两个字符串列表看起来很相似,但它们实际上并不相同。 - egrunin
这是对Mark的想法的改进,保留了结果的array[count]类型,因此结果可以像原始字符串字面值一样在sizeof()中使用:stackoverflow.com/a/63888331/1046167 - Louis Semprini
2个回答

12
使用宏来生成字符串的两种形式,再使用模板函数选择哪个形式。
template<typename C>
const C * ChooseCW(const char * c, const wchar_t * w);

template<>
const char * ChooseCW<char>(const char * c, const wchar_t * w)
{
    return c;
}

template<>
const wchar_t * ChooseCW<wchar_t>(const char * c, const wchar_t * w)
{
    return w;
}

#define CW(C, STR) ChooseCW<C>(STR, L##STR)

insert(value_type(CW(C, "in1"), CW(C, "out1")));

简单,就像所有天才一样。您的代码片段需要进行一些调整才能编译 - 将它们作为我的问题编辑发布即可。但是除此之外 - 简直太棒了。 - mark
@mark,感谢你的赞扬和指出我代码中的错误。希望现在它能编译成功了,尽管它不完全与你的相同。 - Mark Ransom
这里是您想法的改进版本,保留了结果的array[count]类型,因此结果可以像原始字符串文字一样在sizeof()中使用:stackoverflow.com/a/63888331/1046167 - Louis Semprini

-1
将所有字符串常量定义为静态成员,类似于这样:
#include "stddef.h"
#include "stdio.h"
template<class C>
class String
{
  public:
    String(const C* value = defVal) : mValue(value) {}
    const C* valueOf() { return mValue; }
  private:
    const C* mValue;
    static const C defVal[];
};
const char String<char>::defVal[] = "char";
const wchar_t String<wchar_t>::defVal[] = L"wchar_t";
int main(int argc, char **argv)
{
  String<char> c(*argv);
  String<wchar_t> w;
  return printf("%S\n", w.valueOf());
}

你可以使用宏定义来避免重复定义。

他不就是做了那个吗? - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接