重载[]但其结果在与其他运算符交互之前没有解析(c ++)

时间:2013-07-05 19:31:41

标签: c++ templates operator-overloading subscript-operator

我有一个数据对象,我试图让所有运营商都在使用。它是带有可变ptrs的一大块数据,并且具有任意数量的不同类型和大小等等。使用枚举和模板以及switch语句处理类型。因此对于每个x,d [x]是一种类型,具有任意数量的它们并且它们可以是向量。所以d [x] [y]和d [x] [y] [z]。我做了一个内部帮助对象来帮助解决这个问题。所以我有[]重载来做这样的事情,它会返回正确的类型:(gcc 4.6.1)

[编辑:d(x,y,z)也有同样的问题 - 问题不在于[]运算符]

int i = d[0][3][5];

我在这个助手对象中重载了T()。

template <class T> 
data::helper::operator T ();            // switch(x)...return different types
data::helper data::operator [] (int i); // recurse, return helper(t, d, x, i, j);

所以我只返回这个对象,它在那个时候解析了它的类型(切换到与t-> get&lt; char&gt;(d,x,i,j)等相关的情况)。所以问题是,如果我想做这样的事情

int i = d[0][1] + d[4][2];
if (d[5][1] != d[3][0]) ...

然后我最终必须重载每个运算符以接受这个临时数组助手对象。而现在我不得不在某些操作员身上制作临时值,这很痛苦。

基本上,我觉得我需要首先解析运算符T(),然后编译器尝试取两个并添加它们。

无论如何我必须为=和+ =等运算符执行此操作,但我想删除这些jazillion宏,帮助我定义所有其他运算符。

另外,我觉得如果我能以某种方式重载左值运算符,我不用担心=运算符。也许那和&amp;()(现在只返回一个模板化的ptr)。 ...?或者实际上,这更像我的意思,至少对于d [] =某事,但我没有这个工作。我不确定如何将任何类型的ptr转换为此返回值。

data::helper & data::operator [] (int i);

我的大部分工作都在工作,但这是很多代码,而且我认为我将不得不为每个访问添加一个额外的if语句来执行临时工作,我不想这样做。那我错过了什么?

编辑:使用d(x,i,j)与d [x] [i] [j]相同。我很确定我至少在第一个链接中使用的内容的开始部分。发布。问题是在语句中使用之前将最后一个辅助对象解析为其数据。不知何故,编译器想要一个接受辅助对象的运算符,即使它知道如何单独解决它...我想。经过几天的重载每个操作员所以我忘了所有的细节。 :)

但现在的主要问题是这样的事情:

helper operator + (helper & l, helper & r)

我想定义以下内容但它没有被使用 - 那么我认为我的问题可能会得到解决。一元操作的相似故事〜, - 和后缀++, - 。

template <class T> T operator + (helper & l, helper & r)

但是所有这一切只是因为我的T()有一些不足之处。这对我来说大部分都是新的,所以我打赌我错过了一些东西。

1 个答案:

答案 0 :(得分:1)

执行此类操作的实际方法是使用表达式模板。

我会将您的返回值从operator[]更改为表达式模板。

这将使用C ++ 11功能,因为它缩短了它。

enum class ExpressionType { Index, Addition };
template< ExpressionType Op, typename LHS, typename RHS >
struct Expression {
  LHS lhs;
  RHS rhs;
  template<typename T>
  operator T();
};
// to separate out the evaluation code:
template< typename T, ExpressionType Op, typename LHS, typename RHS >
struct Evaluate {
  T operator()( Expression<Op, LHS, RHS> exp ) const;
};
template< ExpressionType Op, typename LHS, typename RHS >
template<typename T>
Expression<Op,LHS,RHS>::operator T() {
  return Evaluate<T,Op,LHS,RHS>()( std::move(*this) );
}
// further specializations needed:
template< typename T, typename RHS >
struct Evaluate< T, ExpressionType::Index, data, RHS > {
  T operator()( Expression<Op, ExpressionType::Index, data, RHS> exp ) const {
    // we just assume RHS can be treated like an integer.  If it cannot,
    // we fail to compile.  We can improve this with SFINAE elsewhere...
    return exp.lhs.get_nth(exp.rhs);
  }
};
template< typename T, typename LHS, typename RHS >
struct Evaluate< T, ExpressionType::Addition, LHS, RHS > {
  T operator()( Expression<Op, ExpressionType::Index, data, RHS> exp ) const {
    // code with all of LHS, RHS and T visible!
  }
};
template<typename E>
struct is_expression : std::false_type {};
template<ExpressionType Op, typename LHS, typename RHS>
struct is_expression<Expression<Op,LHS,RHS> : std::true_type {};
template<ExpressionType Op, typename LHS, typename RHS>
Expression<Op, LHS, RHS> make_expression( LHS&& lhs, RHS&& rhs ) {
  return { std::forward<LHS>(lhs), std::forward<RHS>(rhs) };
}
// here is why I want to start out with returning an expression.  This SFINAE test
// is extremely easy because of that -- we overload operator+ on any two types, so long
// as one of them is an Expression!
template<typename LHS, typename RHS, typename=typename std::enable_if<is_expression<LHS>::value || is_expression<RHS>::value >::type>
ExpressionType<ExpressionType::Addition, LHS, RHS> operator+( LHS&& lhs, RHS&& rhs )
{
  return make_expression<ExpressionType::Addition>(std::forward<LHS>(lhs), std::forward<RHS>(rhs) );
}

所以我们的想法是,我们在编译时构建一个模板树,表示编译器评估各种表达式的顺序。

当我们最终将它转换为具体类型T时,我们才开始评估工作。

这避免了必须创建任何临时对象,但这意味着我们必须做很多模板mojo才能使事情正常运行。以上是这种模板表达式树生成器的草图。

要查看简单案例的完整实现,这里有wikipedia关于该主题的文章的链接,其中构建了一个完整的表达式树系统来进行std::vector向量处理临时对象。