在接受用户定义文字的排列时防止过载爆炸

时间:2017-10-27 19:33:37

标签: c++ c++14

我有一个结构Dimensions,它有宽度和高度的模板化参数:

template<unsigned W, unsigned H>
struct Dimensions
{
    static constexpr unsigned width = W;
    static constexpr unsigned height = H;
};

我有WidthHeight个类,它们的各自值都有模板化参数:

template<unsigned N>
struct Width
{
    static constexpr unsigned value = N;
};

template<unsigned N>
struct Height
{
    static constexpr unsigned value = N;
};

我为宽度和高度创建了用户定义的文字

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

其中to_unsignedparse将字符转换为无符号值

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");
}

因此,我现在可以创建一个函数模板,该模板采用从字符串文字创建的WidthHeight个实例,并返回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, "");

我想让用户只能提供一个维度(另一个维度为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>{};
}

如果我现在添加第三个维度Breadthdimensions重载的数量将会爆炸,因为我目前已经实现了它,每次可能的排列都需要一次重载。

问题:

有没有办法允许用户使用宽度,宽度和高度的任意组合/排序来指定尺寸,省略一些等等?

(关于coliru here的上述代码的工作示例)

1 个答案:

答案 0 :(得分:8)

最直接的方法是从元函数开始搜索特定维度:

SELECT
    COUNT(`user_id`) > 0
FROM
    `user`
GROUP BY
    `user_id`
HAVING
    SUM(CASE WHEN `user`.`email`="admin@admin.com" THEN 1 ELSE 0 END) <> 0 AND
    SUM(CASE WHEN `user`.`user_id`=1 THEN 1 ELSE 0 END) <> 0

然后我们就用它了:

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

更聪明的方法可能是利用继承:

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>();
}

第二个很好的特性是重复的维度是一个编译错误。