2015-10-15 123 views
2

该函数的参数是特定类型的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, ""); 
} 

任何帮助,将不胜感激。

+1

做任何事情,从使用阻止你't.add_field <1>(“”);'到位t.add_field的'(1,“ “);'? –

+1

这是不可能的,因为在编译时标记是未知的。 – 0xBADF00

+0

在编译时您需要模板参数,而add_field的参数在运行时给出。尝试解释目的是什么?可能有更合适的解决方案。 – Taus

回答

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... 
    } 
}; 

callis_acceptablefalse值的存在只是为了避免编译错误,不正确的实例。假设你永远不会拨打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, %%) 
    } 
} 

我没有使用正常传递到宏观,我通常使用句法废话%%表示数据参数。如果你愿意,你可以传递和使用任何东西。

如果提升。预处理是太黑暗魔法给你,你也可以使用模板递归:

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 
    } 
}; 
+0

由于可能有超过一百个标签ID,因此“切换”解决方案并不是一种选择。 – 0xBADF00

+0

@KaptenMugg我已经添加了两个选项来自动生成'switch'(或类似构造)。 – Angew

+0

哇,这正是我想要的真棒,谢谢! – 0xBADF00

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... 
} 

另一种解决办法,虽然有点更复杂的是使用一个tag分派技术 - 但是,在“调度”将是一个编译时间计算。

如果要求是获取运行时类型,那么模板可能根本就不是答案,因为模板必须编译时间代码生成。在这种情况下,变种和某种形式的访问或更传统的多态可能更合适。

+1

*“在这种情况下,constexpr可能很有用。”*,这里“constexpr”有用吗? –

+0

'constexpr'没有用,我忘了它不能用于参数。 – Niall

+0

这不是一个解决方案,因为在编译时标记是未知的。 – 0xBADF00