函数的参数是特定类型的id。该函数将T
中提供的值编码为标记对应的类型的值,因此我想完成以下操作。是否有任何简洁的模板技巧可以使这个工作?
template<int tag_id>
struct tag_traits;
template<>
struct tag_traits<1>
{
typedef char type;
};
template<>
struct tag_traits<2>
{
typedef int type;
};
template<>
struct tag_traits<3>
{
typedef double type;
};
class test {
public:
test()
{}
template <typename T>
void add_field(int tag, T value)
{
using t = typename tag_traits<tag>::type;
// more...
}
};
int main()
{
test t;
t.add_field(1, "");
t.add_field(2, "");
}
任何帮助都将不胜感激。
答案 0 :(得分:3)
template <typename T>
void add_field(int tag, T value)
{
using t = typename tag_traits<tag>::type;
// more...
}
tag_traits<tag>
的参数必须是编译时常量,在这种情况下不是。添加const
也无法解决问题。
解决方案是将tag
移动为模板参数。
template <int tag, typename T>
void add_field(T value)
{
using t = typename tag_traits<tag>::type;
// more...
}
另一个解决方案虽然稍微复杂一点,但是会使用标签调度技术 - 但同样,&#34; dispatch&#34;将是一个编译时计算。
如果要求获取运行时类型,那么模板可能根本就不是答案,因为模板被绑定到编译时代码生成。在这种情况下,变体和某种形式的访问或更传统的多态可能更合适。
答案 1 :(得分:3)
模板参数必须在编译时知道。如果您可以更改函数以将tag
作为模板参数,则它将起作用:
template <int tag, typename T>
void add_field(T value)
{
using t = typename tag_traits<tag>::type;
// more...
}
// ...
int main()
{
test t;
t.add_field<1>("");
t.add_field<2>("");
}
如果这不是一个选项,那么您需要一个switch
或类似的构造:
template <typename T>
void add_field(int tag, T value)
{
switch (tag) {
case 1:
{
using t = typename tag_traits<1>::type;
// more...
break;
}
case 2:
{
using t = typename tag_traits<2>::type;
// more...
break;
}
}
}
问题是所有案例必须在语法和语义上对 T
的所有值有效。如果这也不是一个选项,则需要具有辅助特征的繁重模板机器:
template <int tag, class T>
struct is_acceptable : std::false_type
{};
template <int tag>
struct is_acceptable<tag, typename tag_traits<tag>::type> : std::true_type
{};
// Add any other specialisations as applicable
// ...
template <typename T>
void add_field(int tag, T value)
{
switch (tag) {
case 1:
{
add_field_helper<1, T>::call(value);
break;
}
case 2:
{
add_field_helper<2, T>::call(value);
break;
}
}
}
template <int tag, class T, bool acceptable = is_acceptable<tag, T>::value>
struct add_field_helper
{
static void call(T) {}
};
template <int tag, class T>
struct add_field_helper<tag, T, true>
{
static void call(T value)
{
using t = typename tag_traits<tag>::type;
// more...
}
};
call
false
值的空is_acceptable
仅存在,以防止错误实例化导致编译错误。假设您从未使用无效的add_field
- tag
组合来调用T
,那么它将永远不会在运行时被调用。
如果有太多标签要实际列出,请使用Boost.Preprocessor生成它们。对于有效的代码值0
... MAX_TAG - 1
,可以使用以下代码:
#define ONE_CASE(z, tag, unused) \
case tag: add_field_helper<tag, T>::call(value); break;
template <typename T>
void add_field(int tag, T value)
{
switch (tag) {
BOOST_PP_REPEAT(MAX_TAG, ONE_CASE, %%)
}
}
我没有使用通常传递给宏的数据参数,我通常使用句法无意义%%
来表示。如果您愿意,可以在那里传递和使用任何东西。
如果Boost.Preprocessor对你来说太黑暗了,你也可以使用模板递归:
template <class T>
void add_field(int tag, T value)
{
add_field_dispatcher<0, T>::call(tag, value);
}
template <int one_tag, class T>
struct add_field_dispatcher
{
static void call(int tag, T value)
{
if (tag == one_tag) add_field_helper<one_tag, T>::call(value);
else add_field_dispatcher<one_tag + 1, T>::call(tag, value);
}
};
template <class T>
struct add_field_dispatcher<MAX_TAG, T>
{
static void call(int tag, T value)
{
// Called with unsupported tag
}
};