有没有比 allocator_type 的存在更好的方法来区分可调整大小的容器?
Is there a better way to distinguish resizable containers than presence of allocator_type?
我有 operator>>()
的模板重载,我需要区分可以调整大小的容器(例如 vector
)和不能调整大小的容器(例如 array
)。我目前只是在使用 allocator_type
特性(见下面的代码)——它工作得很好——但想知道是否有更明确的方法来测试它。
template <class T>
struct is_resizable {
typedef uint8_t yes;
typedef uint16_t no;
template <class U>
static yes test(class U::allocator_type *);
template <class U>
static no test(...);
static const bool value = sizeof test<T>(0) == sizeof yes;
};
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value && is_resizable<C>::value,
istream &
>::type
operator>>(istream &ibs, C &c)
{
c.resize(ibs.repeat() == 0 ? c.size() : ibs.repeat());
for (typename C::iterator it = c.begin(); it != c.end(); ++it)
{
C::value_type v;
ibs >> v;
*it = v;
}
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value && !is_resizable<C>::value,
istream &
>::type
operator>>(istream &ibs, C &c)
{
for (typename C::iterator it = c.begin(); it != c.end(); ++it)
ibs >> *it;
return ibs;
}
是的。您需要 define/use 自定义特征(例如 boost::spirit::traits
是)。
分配器的存在与否并不能真正告诉您容器是否是固定大小的。非标准容器可能根本没有 allocator_type
关联类型,但仍允许 resize(...)
事实上,由于您有效地断言了一个允许
的概念
C::resize(size_t)
您可以为此使用表达式 SFINAE
如果您想测试容器是否支持 resize
,您可能应该只检查它是否具有 resize()
功能。在 C++03 中,它看起来像:
template <typename T>
class has_resize
{
private:
typedef char yes;
struct no {
char _[2];
};
template <typename U, U>
class check
{ };
template <typename C>
static yes test(check<void (C::*)(size_t), &C::resize>*);
template <typename C>
static no test(...);
public:
static const bool value = (sizeof(test<T>(0)) == sizeof(yes));
};
现代 C++ 有一种非常简洁的方法:
template <typename T, typename = int>
struct resizable : std::false_type {};
template <typename T>
struct resizable <T, decltype((void) std::declval<T>().resize(1), 0)> : std::true_type {};
现在如果你不需要区分名称为resize
的成员函数和成员变量,你可以将上面的decltype
写成如下:
decltype( (void) &T::resize, 0 )
请注意,转换为 void
是为了处理类型重载逗号运算符和泛化失败的情况(因此它比抱歉策略更安全)
感谢@Jarod42 在另一个问题上的帮助,我有一个适用于 C++98、C++03 和 C++11 的解决方案; g++ 和 VS2015。另外,对于问题儿童,std::vector<bool>
.
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature) \
template <typename U> \
class traitsName \
{ \
private: \
typedef boost::uint8_t yes; typedef boost::uint16_t no; \
template<typename T, T> struct helper; \
template<typename T> static yes check(helper<signature, &funcName>*);\
template<typename T> static no check(...); \
public: \
static const bool value = sizeof check<U>(0) == sizeof(yes); \
}
DEFINE_HAS_SIGNATURE(has_resize_1, T::resize, void (T::*)(typename T::size_type));
DEFINE_HAS_SIGNATURE(has_resize_2, T::resize, void (T::*)(typename T::size_type, \
typename T::value_type));
下面是它的用法。请注意,resize()
的 has_resize_1
和 has_resize_2
成员函数签名均已检查。那是因为在 C++11 之前,resize()
有一个带有两个参数的签名,最后一个带有默认值;从 C++11 开始,它有两个签名——一个有一个参数,另一个有两个参数。此外,VS2015 显然具有 三个 签名——以上所有。解决方案就是始终检查两个签名。
可能有一种方法可以将两个检查组合成一个类型特征,例如has_resize<C>::value
。知道的告诉我吧
template <typename T>
typename boost::enable_if_c<
!boost::spirit::traits::is_container<T>::value,
xstream &>::type
operator>>(xstream &ibs, T &b)
{
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value &&
(has_resize_1<C>::value || has_resize_2<C>::value),
xstream &
>::type
operator>>(xstream &ibs, C &c)
{
typename C::value_type v;
ibs >> v;
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value &&
!(has_resize_1<C>::value || has_resize_2<C>::value),
xstream &
>::type
operator>>(xstream &ibs, C &c)
{
typename C::value_type v;
ibs >> v;
return ibs;
}
我有 operator>>()
的模板重载,我需要区分可以调整大小的容器(例如 vector
)和不能调整大小的容器(例如 array
)。我目前只是在使用 allocator_type
特性(见下面的代码)——它工作得很好——但想知道是否有更明确的方法来测试它。
template <class T>
struct is_resizable {
typedef uint8_t yes;
typedef uint16_t no;
template <class U>
static yes test(class U::allocator_type *);
template <class U>
static no test(...);
static const bool value = sizeof test<T>(0) == sizeof yes;
};
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value && is_resizable<C>::value,
istream &
>::type
operator>>(istream &ibs, C &c)
{
c.resize(ibs.repeat() == 0 ? c.size() : ibs.repeat());
for (typename C::iterator it = c.begin(); it != c.end(); ++it)
{
C::value_type v;
ibs >> v;
*it = v;
}
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value && !is_resizable<C>::value,
istream &
>::type
operator>>(istream &ibs, C &c)
{
for (typename C::iterator it = c.begin(); it != c.end(); ++it)
ibs >> *it;
return ibs;
}
是的。您需要 define/use 自定义特征(例如 boost::spirit::traits
是)。
分配器的存在与否并不能真正告诉您容器是否是固定大小的。非标准容器可能根本没有 allocator_type
关联类型,但仍允许 resize(...)
事实上,由于您有效地断言了一个允许
的概念C::resize(size_t)
您可以为此使用表达式 SFINAE
如果您想测试容器是否支持 resize
,您可能应该只检查它是否具有 resize()
功能。在 C++03 中,它看起来像:
template <typename T>
class has_resize
{
private:
typedef char yes;
struct no {
char _[2];
};
template <typename U, U>
class check
{ };
template <typename C>
static yes test(check<void (C::*)(size_t), &C::resize>*);
template <typename C>
static no test(...);
public:
static const bool value = (sizeof(test<T>(0)) == sizeof(yes));
};
现代 C++ 有一种非常简洁的方法:
template <typename T, typename = int>
struct resizable : std::false_type {};
template <typename T>
struct resizable <T, decltype((void) std::declval<T>().resize(1), 0)> : std::true_type {};
现在如果你不需要区分名称为resize
的成员函数和成员变量,你可以将上面的decltype
写成如下:
decltype( (void) &T::resize, 0 )
请注意,转换为 void
是为了处理类型重载逗号运算符和泛化失败的情况(因此它比抱歉策略更安全)
感谢@Jarod42 在另一个问题上的帮助,我有一个适用于 C++98、C++03 和 C++11 的解决方案; g++ 和 VS2015。另外,对于问题儿童,std::vector<bool>
.
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature) \
template <typename U> \
class traitsName \
{ \
private: \
typedef boost::uint8_t yes; typedef boost::uint16_t no; \
template<typename T, T> struct helper; \
template<typename T> static yes check(helper<signature, &funcName>*);\
template<typename T> static no check(...); \
public: \
static const bool value = sizeof check<U>(0) == sizeof(yes); \
}
DEFINE_HAS_SIGNATURE(has_resize_1, T::resize, void (T::*)(typename T::size_type));
DEFINE_HAS_SIGNATURE(has_resize_2, T::resize, void (T::*)(typename T::size_type, \
typename T::value_type));
下面是它的用法。请注意,resize()
的 has_resize_1
和 has_resize_2
成员函数签名均已检查。那是因为在 C++11 之前,resize()
有一个带有两个参数的签名,最后一个带有默认值;从 C++11 开始,它有两个签名——一个有一个参数,另一个有两个参数。此外,VS2015 显然具有 三个 签名——以上所有。解决方案就是始终检查两个签名。
可能有一种方法可以将两个检查组合成一个类型特征,例如has_resize<C>::value
。知道的告诉我吧
template <typename T>
typename boost::enable_if_c<
!boost::spirit::traits::is_container<T>::value,
xstream &>::type
operator>>(xstream &ibs, T &b)
{
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value &&
(has_resize_1<C>::value || has_resize_2<C>::value),
xstream &
>::type
operator>>(xstream &ibs, C &c)
{
typename C::value_type v;
ibs >> v;
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value &&
!(has_resize_1<C>::value || has_resize_2<C>::value),
xstream &
>::type
operator>>(xstream &ibs, C &c)
{
typename C::value_type v;
ibs >> v;
return ibs;
}