2015-09-02 57 views
1
选择模板返回类型

我有一些工作,但它似乎非常详细。从参数

#include <array> 
#include <iostream> 
#include <type_traits> 

using DataArrayShort = std::array<unsigned char, 4>; 
using DataArrayLong = std::array<unsigned char, 11>; 

// Two base classes the later template stuff should choose between 
class Short 
{ 
public: 
    Short(const DataArrayShort & data) { /* do some init */} 
}; 

class Long 
{ 
public: 
    Long(const DataArrayLong & data) { /* do some init */} 
}; 

// Concrete derived of the two bases 
class S1 : public Short 
{ 
public: 
    using Short::Short; 
    operator std::string() { return "S1!";} 
}; 

class S2 : public Short 
{ 
public: 
    using Short::Short; 
    operator std::string() { return "S2!";} 
}; 

class L1 : public Long 
{ 
public: 
    using Long::Long; 
    operator std::string() { return "L1!";} 
}; 

class L2 : public Long 
{ 
public: 
    using Long::Long; 
    operator std::string() { return "L2!";} 
}; 

// Variables that will be modified by parsing other things before calling parse<>() 
bool shortDataSet = false; 
bool longDataSet = false; 
DataArrayShort shortData; 
DataArrayLong longData; 

// Begin overly verbose template stuff 
template<bool IsShort, bool IsLong> 
bool getFlag(); 

template<> 
bool getFlag<true, false>() 
{ 
    return shortDataSet; 
} 

template<> 
bool getFlag<false, true>() 
{ 
    return longDataSet; 
} 


template<bool IsShort, bool IsLong> 
struct RetType 
{}; 

template<> 
struct RetType<true, false> 
{ 
    typedef DataArrayShort & type; 
}; 

template<> 
struct RetType<false, true> 
{ 
    typedef DataArrayLong & type; 
}; 

template<bool IsShort, bool IsLong> 
typename RetType<IsShort, IsLong>::type getData(); 

template<> 
DataArrayShort & getData<true, false>() 
{ 
    return shortData; 
} 

template<> 
DataArrayLong & getData<false, true>() 
{ 
    return longData; 
} 

template<typename T> 
inline std::string parse() 
{ 
    // First test if I can create the type with initialized data 
    if  (getFlag<std::is_base_of<Short, T>::value, std::is_base_of<Long, T>::value>()) 
    { 
     // If it's initialized, Then create it with the correct array 
     T t(getData<std::is_base_of<Short, T>::value, std::is_base_of<Long, T>::value>()); 
     return t; 
    } 
    else 
    { 
     return "with uninitialized data"; 
    } 
} 
// End overly verbose template stuff 

int main(int argc, const char * argv[]) 
{ 
    // Something things that may or may not set shortDataSet and longDataSet and give shortData and longData values 

    std::cout << parse<S1>() << std::endl; 

    shortDataSet = true; 

    std::cout << parse<S1>() << std::endl; 

    std::cout << parse<L2>() << std::endl; 

    longDataSet = true; 

    std::cout << parse<L2>() << std::endl; 
} 

对我很重要的语法是parse()。在解析中,我想确保路由到正确的标志和数据来实例化ConcreteType。

我开始认为我不能使用函数模板来做我想做的事情 - 我最好使用具有静态函数成员的类模板。

使用std :: is_base_of似乎笨拙 - 我可以使用内置继承与重载,而不是基于Short和Long的重载is_base_of?

RetType似乎没有必要,但似乎没有其他方法来声明getData()。

部分难点在于我需要在实例化之前确定要初始化t的数据。

我不喜欢IsShort和IsLong的单独模板布尔 - 它不会缩放。

我该怎么办才能收紧这个呢?

+0

如果您将所有内容集中到一个单独的类模板中,这样的事情会更易于管理。 –

回答

0

我可以建议使用特征技术,就像其他答案一样。但我的解决方案是更好的方式,它允许该解决方案的scability,我的意思是在你的代码没有更多true, false, ...标志;)

所以从这个评论开始:

// Variables that will be modified by parsing other things before calling parse<>() 

更改你的代码更具可扩展性版。

首先连接基本类型与数据类型:

template <typename BaseType> 
class BaseDataTypeTraits; 
template <> struct BaseDataTypeTraits<Short> 
{ 
    typedef DataArrayShort DataType; 
}; 
template <> struct BaseDataTypeTraits<Long> 
{ 
    typedef DataArrayLong DataType; 
}; 

然后定义你的基地型性状:

template <typename BaseType> 
struct BaseParseTypeTraits 
{ 
    static bool dataSet; 
    typedef typename BaseDataTypeTraits<BaseType>::DataType DataType; 
    static DataType data; 
}; 

template <typename BaseType> 
bool BaseParseTypeTraits<BaseType>::dataSet = false; 
template <typename BaseType> 
typename BaseParseTypeTraits<BaseType>::DataType BaseParseTypeTraits<BaseType>::data; 

和解析特征为每个特定的基本类型:

template <typename T, typename EnableIf = void> 
class ParseTypeTraits; 

template <typename T> 
class ParseTypeTraits<T, typename std::enable_if<std::is_base_of<Short, T>::value>::type> 
    : public BaseParseTypeTraits<Short> 
{}; 
template <typename T> 
class ParseTypeTraits<T, typename std::enable_if<std::is_base_of<Long, T>::value>::type> 
    : public BaseParseTypeTraits<Long> 
{}; 

而且那么你的解析与其他“特质”几乎相同:

template<typename T> 
inline std::string parse() 
{ 
    typedef ParseTypeTraits<T> TTraits; 
    // First test if I can create the type with initialized data 
    if (TTraits::dataSet) 
    { 
     // If it's initialized, Then create it with the correct array 
     T t(TTraits::data); 
     return t; 
    } 
    else 
    { 
     return "with uninitialized data"; 
    } 
} 


int main(int argc, const char * argv[]) 
{ 
    // Something things that may or may not set shortDataSet and longDataSet and give shortData and longData values 

    std::cout << parse<S1>() << std::endl; 

    BaseParseTypeTraits<Short>::dataSet = true; 

    std::cout << parse<S1>() << std::endl; 

    std::cout << parse<L2>() << std::endl; 

    BaseParseTypeTraits<Long>::dataSet = true; 

    std::cout << parse<L2>() << std::endl; 
} 

工作实施例:ideone

[UPDATE]

在此示例代码我还添加什么是需要添加新的基本和数据类型。

我的意思是你有这样的:

using DataArrayNew = std::array<unsigned char, 200>; 
class New 
{ 
public: 
    New(const DataArrayNew & data) { /* do some init */} 
}; 
class N1 : public New 
{ 
public: 
    using New::New; 
    operator std::string() { return "N1!";} 
}; 

而为了让这些类型由您解析的支持 - 你只需要两个专业化:

template <> struct BaseDataTypeTraits<New> 
{ 
    typedef DataArrayNew DataType; 
}; 
template <typename T> 
class ParseTypeTraits<T, typename std::enable_if<std::is_base_of<New, T>::value>::type> 
    : public BaseParseTypeTraits<New> 
{}; 

这可以被封闭在一个宏:

#define DEFINE_PARSE_TRAITS_TYPE(BaseTypeParam, DataTypeParam) \ 
template <> struct BaseDataTypeTraits<BaseTypeParam>   \ 
{                \ 
    typedef DataTypeParam DataType;       \ 
};                \ 
template <typename T>           \ 
class ParseTypeTraits<T,          \ 
    typename std::enable_if<          \ 
      std::is_base_of<BaseTypeParam, T>::value>::type> \ 
    : public BaseParseTypeTraits<BaseTypeParam>     \ 
{} 

因此,对于新类型的支持很简单,只要这样的:

DEFINE_PARSE_TRAITS_TYPE(New, DataArrayNew); 

更简化的时候可以达到我们要求的基本类型有其数据类型的类定义中定义的 - 喜欢这里:

class New 
{ 
public: 
    typedef DataArrayNew DataType; 
    New(const DataArrayNew & data) { /* do some init */} 
}; 

然后我们就可以有通用BaseDataTypeTraits定义:

template <typename BaseType> 
struct BaseDataTypeTraits 
{ 
    typedef typename BaseType::DataType DataType; 
}; 

所以对于新型号 - 您只需要为DataTypeTraits添加专业化:

template <typename T> 
class ParseTypeTraits<T, typename std::enable_if<std::is_base_of<New, T>::value>::type> 
    : public BaseParseTypeTraits<New> 
{}; 
+0

我最终实现的是最接近你的,但略有不同 - 'template struct BaseParseTypeTraits { static bool dataSet; typedef T dataType; 静态T数据; }; template bool BaseParseTypeTraits :: dataSet = false; template typename BaseParseTypeTraits :: dataType BaseParseTypeTraits :: data;'然后专门研究ParseTypeTraits从DataArrayLong或DataArrayShort中派生的位置。我试图发布一个修改,但它被拒绝了。 – bizaff

+0

@bizaff我使用了一些改进来编辑我的答案。我还加了一些新的想法...... HTH – PiotrNycz

2

你应该转发到启用了SFINAE的调度程序。正向

template <int I> struct chooser : chooser<I-1> { }; 
template <> struct chooser<0> { }; 

它:启动与继承树

template <typename T> 
std::string parse() { return parse_impl<T>(chooser<2>{}); } 

写你的情况:从Short

template <typename T, 
      typename = std::enable_if_t<std::is_base_of<Short, T>::value> 
      > 
std::string parse_impl(chooser<2>) { // (1) 
    // we're a Short! 
    if (shortDataSet) { 
     return T{shortData}; 
    } 
    else { 
     return "with uninitialized data"; 
    } 
} 

template <typename T, 
      typename = std::enable_if_t<std::is_base_of<Long, T>::value> 
      > 
std::string parse_impl(chooser<1>) { // (2) 
    // we're a Long! 
    if (longDataSet) { 
     return T{longData}; 
    } 
    else { 
     return "with uninitialized data"; 
    } 
}  

template <typename > 
std::string parse_impl(chooser<0>) { // (3) 
    // base case 
    return "with uninitialized data"; 
} 

如果T继承,(1)被调用。否则,如果它继承自Long,则调用(2)。否则,调用(3)。这是做SFINAE多个潜在重叠标准的便捷方式

+0

如果它不是Short或Long,它应该编译失败。短或长可能会发生未初始化的数据。 – bizaff

+0

@bizaff然后你可以简单地删除'(3)'过载。没有后备,所以它不会编译。 – Barry

2

重构的一点点走一段很长的路要走(因为你可以,毕竟,无论从ShortLong权继承?):

template<class T, bool IsShort = std::is_base_of<Short, T>::value, 
        bool IsLong = std::is_base_of<Long, T>::value> 
struct data_traits { }; 

template<class T> 
struct data_traits<T, true, false> { 
    static bool getFlag() { return shortDataSet; } 
    static DataArrayShort & getData() { return shortData; } 
}; 

template<class T> 
struct data_traits<T, false, true> { 
    static bool getFlag() { return longDataSet; } 
    static DataArrayLong & getData() { return longData; } 
}; 

template<typename T> 
inline std::string parse() 
{ 
    using traits = data_traits<T>; 
    // First test if I can create the type with initialized data 
    if (traits::getFlag()) 
    { 
     // If it's initialized, Then create it with the correct array 
     T t(traits::getData()); 
     return t; 
    } 
    else 
    { 
     return "with uninitialized data"; 
    } 
}