我正在尝试实现一个类似于std :: pair的类,但具有两个以上的组件。由于在我的应用程序中可能会在编译时就已经知道一些元组组件,因此我想进行以下空间优化:当我知道某个组件是编译时常量时,只需将其声明为' static const'成员,这样就不会在单个类实例中浪费存储空间。 const限定符确保至少在排除不礼貌的const_cast的情况下,任何在运行时修改该值的尝试都会导致编译错误。
我结束了以下实现,一个ntuple类
template<typename T0_ = void,
typename T1_ = void,
typename T2_ = void,
typename T3_ = void
> class ntuple;
和一个用于标记编译时常量的类
template<class type_, type_ value_> class constant
{
typedef type_ type;
static const type value = value_;
};
和一堆ntuple类的部分专业化
template<typename T0_>
class ntuple<
T0_
> {
public:
static const int n=1;
typedef T0_ T0;
static const bool is_static = false;
static const bool is_static0 = false;
T0_ i0;
};
template<
typename T0_, T0_ value0_
>
class ntuple<
constant<T0_, value0_>
> {
public:
static const int n=1;
typedef T0_ T0;
static const bool is_static = true;
static const bool is_static0 = true;
static const T0_ i0 = value0_;
};
template<
typename T0_, T0_ value0_
> const T0_ ntuple<
constant<T0_, value0_> >::i0;
template<
typename T0_,
typename T1_
>
class ntuple<
T0_,
T1_
> {
public:
static const int n=2;
typedef T0_ T0;
typedef T1_ T1;
static const bool is_static = false;
static const bool is_static0 = false;
static const bool is_static1 = false;
T0_ i0;
T1_ i1;
};
template<
typename T0_,
typename T1_, T1_ value1_
>
class ntuple<
T0_,
constant<T1_, value1_>
> {
public:
static const int n=2;
typedef T0_ T0;
typedef T1_ T1;
static const bool is_static = false;
static const bool is_static0 = false;
static const bool is_static1 = true;
T0_ i0;
static const T1_ i1 = value1_;
};
template<
typename T0_,
typename T1_, T1_ value1_
> const T1_ ntuple<
T0_,
constant<T1_, value1_> >::i1;
template<
typename T0_, T0_ value0_,
typename T1_
>
class ntuple<
constant<T0_, value0_>,
T1_
> {
public:
static const int n=2;
typedef T0_ T0;
typedef T1_ T1;
static const bool is_static = false;
static const bool is_static0 = true;
static const bool is_static1 = false;
static const T0_ i0 = value0_;
T1_ i1;
};
template<
typename T0_, T0_ value0_,
typename T1_
> const T0_ ntuple<
constant<T0_, value0_>,
T1_ >::i0;
template<
typename T0_, T0_ value0_,
typename T1_, T1_ value1_
>
class ntuple<
constant<T0_, value0_>,
constant<T1_, value1_>
> {
public:
static const int n=2;
typedef T0_ T0;
typedef T1_ T1;
static const bool is_static = true;
static const bool is_static0 = true;
static const bool is_static1 = true;
static const T0_ i0 = value0_;
static const T1_ i1 = value1_;
};
template<
typename T0_, T0_ value0_,
typename T1_, T1_ value1_
> const T0_ ntuple<
constant<T0_, value0_>,
constant<T1_, value1_> >::i0;
template<
typename T0_, T0_ value0_,
typename T1_, T1_ value1_
> const T1_ ntuple<
constant<T0_, value0_>,
constant<T1_, value1_> >::i1;
这样,标记为constant 的成员将不存储为类成员,从而减小了对象的大小。所需的部分专业化的数量可能非常庞大,对于N = 1,2,3,4,只有2 ^ N个,我最多只报告N = 2:我编写了一个简单的脚本来生成所有这些。该类可以如下使用
ntuple<int, int, bool> tup1;
tup1.i0=2;
tup1.i1=0;
tup1.i2=true;
assert (tup1.i0==2);
assert (tup1.i1==0);
assert (tup1.i2==true);
ntuple<int, constant<int, 3>, constant<bool, false> > tup2;
tup2.i0=2;
// tup2.i1=0; // cannot be assigned, is static a constant
// tup2.i2=true; // cannot be assigned, is static a constant
assert (tup2.i0==2);
assert (tup2.i1==3);
assert (tup2.i2==false);
assert (sizeof(tup1)>sizeof(tup2));
像这样,该班级的作品非常完美。现在,我只想改善声明
ntuples的语法如下
ntuple<int, int_<3>, bool_<true> >
代替
ntuple<int, constant<int, 3>, constant<bool, true> >
其中int_和bool_可以定义为
template<int i> struct int_ : constant<int, i> {};
template<bool b> struct bool_ : constant<bool, b> {};
或者我可以只使用boost :: mpl类似物,这不是重点。为此,简单的解决方案是编写另一个脚本并为常量和非常量模板参数的所有排列生成所有可能的专业化,其中常量模板参数可以是int_,bool_,char_等。这是可行的,但要付出代价部分专业化数量的阶乘增加。我正在考虑如下更改ntuple类的定义
template<typename T0_ = void,
typename T1_ = void,
typename T2_ = void,
typename T3_ = void,
bool const0 = is_const<T0_>::value,
bool const1 = is_const<T1_>::value,
bool const2 = is_const<T2_>::value,
bool const3 = is_const<T3_>::value
> class ntuple;
与
template <class T> is_const { static const bool value = false; };
template <int i> is_const<int_<i> > { static const bool value = true; };
template <bool b> is_const<bool_<b> > { static const bool value = true; };
并将ntuple专门化如下
template<typename T0_,
typename T1_,
typename T2_,
typename T3_> class ntuple<T0_,T1_,T2_,T3_,false,false,false,false> { ... };
template<typename T0_,
typename T1_,
typename T2_,
typename T3_> class ntuple<T0_,T1_,T2_,T3_,true,false,false,false> { ... };
等等。这会将部分专业化的数量减少到与以前相同的数量,并且只需要针对每种有效的“常数”类型专门化traits类。问题是,我想避免额外的模板参数。我可以通过继承来实现,定义一个辅助类
template<typename T0_ = void,
typename T1_ = void,
typename T2_ = void,
typename T3_ = void,
bool const0 = is_const<T0_>::value,
bool const1 = is_const<T1_>::value,
bool const2 = is_const<T2_>::value,
bool const3 = is_const<T3_>::value
> class ntuple_impl;
如上所述专攻然后
template <class T0, class T1, class T2, class T3>
class ntuple : ntuple_impl<T0, T1, T2, T3,
is_const<T0>::value,
is_const<T1>::value,
is_const<T2>::value,
is_const<T3>::value> { ... };
但我想避免继承,因为在某些情况下,结果对象将大于所需的对象,因为它将包含ntuple_impl作为子对象。我想知道是否还有解决此问题的方法。谢谢。朱利亚诺
最佳答案
即使这个问题已经使用了一年多,但我认为它应该得到正确的答案。
遵循Xeo的想法,您可以以一种简单得多的方式完全执行您想要的操作。
在这里,我假设您需要纯c ++ 03。
首先,声明一个仅用作占位符的类型
struct _;
还声明要解释为静态常量的类型。
template<typename T, T>
struct constant;
注意,以上两个都不需要定义。
下一步是定义将表示元组元素的类型,即实际上包含数据的类型。
template<typename T>
struct element
{
typedef T type;
type value;
};
template<>
struct element<_>;
关键是要专门为
element
和所需的其他任何类型(例如引用的constant
和int_
)bool_
。template<typename T, T val>
struct element<constant<T, val> >
{
typedef T const type;
static type value = val;
};
template<typename T, T val>
typename element<constant<T, val> >::type element<constant<T, val> >::value;
从这一点出发,定义
ntuple
很简单。template<typename T0_ = _, typename T1_ = _, typename T2_ = _, typename T3_ = _>
struct ntuple : element<T0_>, ntuple<T1_, T2_, T3_, _>
{
typedef element<T0_> head;
typedef ntuple<T1_, T2_, T3_, _> tail;
};
//nil
template<>
struct ntuple<_, _, _, _>
{};
要访问
ntuple
存储的元数据,可以使用元函数get_type
template<std::size_t n, typename T>
struct get_type;
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
struct get_type<n, ntuple<T0, T1, T2, T3> > : get_type<n-1, typename ntuple<T0, T1, T2, T3>::tail>
{};
template<typename T0, typename T1, typename T2, typename T3>
struct get_type<0, ntuple<T0, T1, T2, T3> >
{
typedef typename ntuple<T0, T1, T2, T3>::head::type type;
};
同样,要访问运行时数据,可以使用函数
get_value
template<bool cond, typename T>
struct enable_if
{
};
template<typename T>
struct enable_if<true, T>
{
typedef T type;
};
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<n, typename get_type<n, ntuple<T0, T1, T2, T3> >::type&>::type
get_value(ntuple<T0, T1, T2, T3>& tuple)
{
return get_value<n-1>(static_cast<typename ntuple<T0, T1, T2, T3>::tail&>(tuple));
}
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<!n, typename ntuple<T0, T1, T2, T3>::head::type&>::type
get_value(ntuple<T0, T1, T2, T3>& tuple)
{
return static_cast<typename ntuple<T0, T1, T2, T3>::head&>(tuple).value;
}
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<n, typename get_type<n, ntuple<T0, T1, T2, T3> >::type const&>::type
get_value(ntuple<T0, T1, T2, T3> const& tuple)
{
return get_value<n-1>(static_cast<typename ntuple<T0, T1, T2, T3>::tail const&>(tuple));
}
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<!n, typename ntuple<T0, T1, T2, T3>::head::type const&>::type
get_value(ntuple<T0, T1, T2, T3> const& tuple)
{
return static_cast<typename ntuple<T0, T1, T2, T3>::head const&>(tuple).value;
}
要计算存储在元组中的元素数量,有一个名为
get_size
的函数template<std::size_t n, typename T>
struct get_size_impl;
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
struct get_size_impl<n, ntuple<T0, T1, T2, T3> > :
get_size_impl<n+1, typename ntuple<T0, T1, T2, T3>::tail>
{
};
template<std::size_t n>
struct get_size_impl<n, ntuple<_, _, _, _> >
{
static std::size_t const value = n;
};
template<std::size_t n>
std::size_t const get_size_impl<n, ntuple<_, _, _, _> >::value;
template<typename T0, typename T1, typename T2, typename T3>
std::size_t get_size(ntuple<T0, T1, T2, T3> const&)
{
return get_size_impl<0, ntuple<T0, T1, T2, T3> >::value;
}
最后,
operator <<
的重载用于打印template<typename Char, typename CharTraits, typename T0, typename T1, typename T2, typename T3>
void print_element(std::basic_ostream<Char, CharTraits>& ostream, ntuple<T0, T1, T2, T3> const& tuple)
{
ostream << static_cast<typename ntuple<T0, T1, T2, T3>::head const&>(tuple).value;
if(get_size(tuple) > 1)
ostream << std::basic_string<Char, CharTraits>(", ");
print_element(ostream, static_cast<typename ntuple<T0, T1, T2, T3>::tail const&>(tuple));
}
template<typename Char, typename CharTraits>
void print_element(std::basic_ostream<Char, CharTraits>& ostream, ntuple<_, _, _, _> const&)
{
}
template<typename Char, typename CharTraits, typename T0, typename T1, typename T2, typename T3>
std::basic_ostream<Char, CharTraits>& operator <<(std::basic_ostream<Char, CharTraits>& ostream, ntuple<T0, T1, T2, T3> const& tuple)
{
ostream << Char('<');
print_element(ostream, tuple);
ostream << Char('>');
}
以下用例说明了
ntuples
的用法,并明确说明已实现了目标优化。int main()
{
ntuple<char, int, long> a;
ntuple<constant<char, '8'>, int, constant<long, 888> > b;
ntuple<constant<char, '9'>, constant<int, 99>, constant<long, 999> > c;
assert(sizeof(a) > sizeof(b));
assert(sizeof(b) > sizeof(c));
get_value<0>(a) = '1';
get_value<1>(a) = 10;
get_value<2>(a) = 100;
// get_value<0>(b) = '2'; //assignment of read-only location
get_value<1>(b) = 20;
// get_value<2>(b) = 200; //assignment of read-only location
// get_value<0>(c) = '3'; //assignment of read-only location
// get_value<1>(c) = 30; //assignment of read-only location
// get_value<2>(c) = 300; //assignment of read-only location
std::cout << std::endl;
std::cout << "a = " << a << ", length: " << get_size(a) << ", size in bytes: " << sizeof(a) << std::endl;
std::cout << "b = " << b << ", length: " << get_size(b) << ", size in bytes: " << sizeof(b) << std::endl;
std::cout << "c = " << c << ", length: " << get_size(c) << ", size in bytes: " << sizeof(c) << std::endl;
}
这个简单的案例涵盖了最多包含4个元素的结节。将其扩展到所需的任意数量(并由所使用的编译器支持)应该很简单。实际上,使用这种方法不需要任何脚本来生成代码。