我有一个结构Dimensions,其中包含宽度和高度的模板参数:
template<unsigned W, unsigned H>
struct Dimensions
{
static constexpr unsigned width = W;
static constexpr unsigned height = H;
};
Run Code Online (Sandbox Code Playgroud)
我有Width和Height类有各自值的模板参数:
template<unsigned N>
struct Width
{
static constexpr unsigned value = N;
};
template<unsigned N>
struct Height
{
static constexpr unsigned value = N;
};
Run Code Online (Sandbox Code Playgroud)
我已经为宽度和高度创建了用户定义的文字
template<char... cs>
constexpr auto operator""_w() -> Width<to_unsigned(0, parse(cs)...)>
{
return {};
}
template<char... cs>
constexpr auto operator""_h() -> Height<to_unsigned(0, parse(cs)...)>
{
return {};
}
Run Code Online (Sandbox Code Playgroud)
在哪里to_unsigned并将parse字符转换为无符号值
constexpr unsigned to_unsigned(unsigned p)
{
return p;
}
template<class... Ts>
constexpr unsigned to_unsigned(unsigned val, unsigned v, Ts... vs)
{
return to_unsigned(val * 10 + v, vs...);
}
constexpr unsigned parse(char C)
{
return (C >= '0' && C <= '9')
? C - '0'
: throw std::out_of_range("input is not a number");
}
Run Code Online (Sandbox Code Playgroud)
因此,我现在可以创建一个函数模板,它从字符串文字中获取Width和Height实例,并返回一个Dimensions
template<unsigned W, unsigned H>
constexpr Dimensions<W, H> dimensions(Width<W>, Height<H>)
{
return Dimensions<W, H>{};
}
auto d = dimensions(5_w, 10_h);
static_assert(d.width == 5, "");
static_assert(d.height == 10, "");
Run Code Online (Sandbox Code Playgroud)
我想允许用户只能提供一个维度(另一个是0),或者以不同的顺序提供文字.
我目前实现这一点的方法是有许多不同的重载:
// width, height
template<unsigned W, unsigned H = 0>
constexpr Dimensions<W, H> dimensions(Width<W>, Height<H>)
{
return Dimensions<W, H>{};
}
// height, width
template<unsigned H, unsigned W = 0>
constexpr Dimensions<W, H> dimensions(Height<H>, Width<W>)
{
return Dimensions<W, H>{};
}
Run Code Online (Sandbox Code Playgroud)
如果我现在添加第三个维度,过载Breadth的数量dimensions将会爆炸,因为我现在已经实现了它,每次可能的排列需要一个重载.
题:
有没有办法允许用户使用宽度,宽度和高度的任何组合/排序指定尺寸,省略一些等?
(工作实施例上coliru上面的代码在这里)
最直接的方法是从元函数开始搜索特定维度:
template <template <unsigned> class Z, class... Ts>
struct find_dimension;
template <template <unsigned> class Z, class... Ts>
using find_dimension_t = typename find_dimension<Z, Ts...>::type;
// found it
template <template <unsigned> class Z, unsigned N, class... Ts>
struct find_dimension<Z, Z<N>, Ts...> {
using type = Z<N>;
};
// keep going
template <template <unsigned> class Z, class T, class... Ts>
struct find_dimension<Z, T, Ts...>
: find_dimension<Z, Ts...>
{ };
// default to 0
template <template <unsigned> class Z>
struct find_dimension<Z> {
using type = Z<0>;
};
Run Code Online (Sandbox Code Playgroud)
然后我们就用它:
template <class... Dimensions>
auto dimensions(Dimensions... ) {
using height = find_dimension_t<Height, Dimensions...>;
using width = find_dimension_t<Width, Dimensions...>;
using breadth = find_dimension_t<Breadth, Dimensions...>;
return Dimensions<width::value, height::value, breadth::value>();
}
Run Code Online (Sandbox Code Playgroud)
更聪明的方法可能是利用继承:
template <template <unsigned> class Z, unsigned N>
constexpr unsigned get_dim(Z<N> d) { return N; }
template <template <unsigned> class Z>
constexpr unsigned get_dim(... ) { return 0; }
template<class... DimensionTs>
auto dimensions(DimensionTs...)
{
struct X : DimensionTs... { };
static constexpr X x {};
return Dimensions<
get_dim<Width>(x),
get_dim<Height(x),
get_dim<Breadth>(x)
>();
}
Run Code Online (Sandbox Code Playgroud)
第二个很好的特性是重复的维度是一个编译错误.