参数化运算符重载

时间:2016-04-27 08:33:38

标签: c++ operator-overloading template-meta-programming higher-order-functions argument-dependent-lookup

我的库有几个操作,自然映射到算术和逻辑/布尔运算符。但是,除了lhsrhs之外,这些操作还需要其他参数。

以下是我用ClipperLib演示的几何示例:
让我们说我想创建一个多边形偏移运算符,它采用2D多边形和偏移delta并执行多边形偏移。这个例子来自ClipperLib: enter image description here
在数学上,此操作可以映射到采用多边形类型和数字类型的operator+ 但是,基础实现需要更多参数,例如:缓和,精确等。

我想将数学符号的力量,表现力和简洁性与底层实现的控制和精确性结合起来。我想写:

// somehow materialize a properly parametrized operator+=()
poly += 10;

并且这样做就是我想要的。

将这些参数注入二进制重载运算符有几个选项,但每个都有自己的缺点:

  1. 设置并使用class-static / global(或thread local)参数 但是,使用全局变量对于线程,可发现性,可读性和清理都是不利的。
  2. 将额外参数保留为操作员输入类型的成员 这会给调用者带来类型包装负担,并可能使代码更加麻烦。在一致性方面,这一点也不清楚哪些输入类型应具有这些参数以及在两者的冲突或多个参数值的情况下会发生什么。
    另外,如果我想多次执行操作,我必须反复重新指定参数。
  3. 有人可以提出另一种方法吗?

    我正在考虑但尚不确定如何工作的一个选项是将重载的运算符作为模板函数放在单独的命名空间中。模板将在所需的参数上进行参数化(假设它们在编译时已知),并且一旦实例化,操作符就会以某种方式被拉入当前可发现的命名空间,其中ADL将正确匹配它。 我不确定是否/如何解决这个问题。

    或者,使用相同的命名空间隐藏和显示技巧为操作员输入类型添加自动类型转换,以便它们自动用提供额外参数的类型包装。

    这种类型的命名空间是否可以隐藏和暴露+ ADL?

    我想这有点像给操作员一个类似lambda的行为。

2 个答案:

答案 0 :(得分:2)

我的目标是支持这样的事情:

poly += miter(10).limit(3)

那是:

class modifier
{
public:
    virtual polygon& add(polygon&) const = 0;
};

class miter : public modifier
{
public:
    miter(int width);
    miter& limit(int ml);

    virtual polygon& add(polygon&) const override;
};

polygon& operator +=(polygon& poly, const modifier& mod)
{
    return mod.add(poly);
}

如果要对多个多边形应用相同的操作,可以执行以下操作:

miter mod(10);
mod.limit(3);
polyA += mod;
polyB += mod;
polyC += mod;

答案 1 :(得分:1)

我提前警告说我的解决方案有点hackish和uscaleable。但是,如果唯一的目的是减少用户表达的冗长程度..你可以做类似于following的事情:

template<class Space>
class numeric
{
  long double _val;
public:
  numeric (long double val) : _val(val) {}
  operator long double() { return _val; }
};

struct s_mither;
numeric<s_mither> operator ""_mtr(long double v) { return v; }

enum class end_token_t : char {};
constexpr end_token_t end_token{};

class entity
{
  struct op_params_t
  {
    long double _scalar;
    long double _mither;
    bool        _in_op;

  } op_params;

public:
  entity& operator+= (long double scalar)
  { op_params._in_op = true; op_params._scalar = scalar; return *this;}

  entity& operator+= (numeric<s_mither> mither)
  { op_params._in_op = true; op_params._mither = mither; return *this;}

  entity& operator, (long double scalar)
  { if(op_params._in_op) op_params._scalar = scalar; return *this;}

  entity& operator, (numeric<s_mither> mither)
  { if(op_params._in_op) op_params._mither = mither; return *this;}

  void    operator, (end_token_t)
  { op_params._in_op = false; }
};


int main() {
    entity e;

    e += 10.0, 1.0_mtr, end_token;
    e += 20._mtr, 5, end_token;

    return 0;
}

它很丑陋,但有所有参数都是可选的好处。 可悲的是,我无法绕过令牌。

更好的解决方案可能是提供一个启动令牌,它返回一个临时的收集器&#34;第一个逗号上的对象。这样,您可以将所有逗号运算符重载到实际实体类之外。