0

I'm trying to write an expression template and I've run into a problem I don't know how to solve. I've read C++ Templates: The Complete Guide but they don't seem to address this question.

As an example, consider a expression template for a set type set (of integers) with the standard set operations intersection, union, negation, xor, difference, etc. These functions all have efficient implementations in terms of iterators, so I want my expression template classes to have an iterator-like interface. For example,

class set_expr_set
{
  set::iter i;

  set_expr_set (const set &s) : i(s) { }

  operator bool () const { return (bool)i; }
  void operator ++ () { i ++; }
  int val () { return *i; }  
}

and then I have expression template classes set_expr_union, etc. Now, the problem is, the objects created corresponding to expression templates expression are all temporaries and therefore const, but to evaluate the expression I need to iterate through the values (calling ++ and val), and these are non-const. I can't declare set::operator = (set_expr &) as non-const, because the temporaries won't bind a non-const parameter. I could cast away the const-ness in operator =, but that doesn't feel like the right solution.

I'm my example doesn't have enough details to make the problem clear, I'll be happy to clarify.

EDIT: Here are some more details. Suppose set_expr_union and set_expr_intersection have the above interface, too: operator ++, val and operator bool. Also, suppose I have

template<class T>
class set_expr
{
  T t;
  ...;
}

where T is intended to be one of set_expr_union, etc. and set_expr also exports t's ++, val, bool interface.

The expression template objects arise through various operators, e.g.:

template<class T1, class T2>
set_expr<set_expr_intersection>
operator & (const set_expr<T1> &e1, const set_expr<T2> &e2)
{
  return set_expr<set_expr_intersection> (set_expr_intersection (e1.t, e2.t));
}

Really, the temporary corresponding to the return value from the operators is where the problem lies.

Now, consider

class set
{
  ...;

  template<class T>
  set &operator = (const set_expr<T> &e)
  {
    clear ();
    for (; e; e ++)
      add_element (e.val ());
  }
};

which I want to use in something like set3 = set1 & set2.

This is the kind of code I want to write.

piotr
  • 5,657
  • 1
  • 35
  • 60
Cotton Seed
  • 363
  • 3
  • 13
  • 1
    Temporaries don't have to be `const` unless you want them to be. Can you show an example of an expression that you are trying to get to work? – CB Bailey Dec 27 '11 at 23:12
  • 1
    Also consider writing your expression templates on top of [Boost.Proto](http://www.boost.org/libs/proto/) to avoid these sorts of problems. – ildjarn Dec 27 '11 at 23:14
  • This is rather vague. Temporaries aren't always const. Assuming `set` is some kind of container like `std::set` then it should have a non-const iterator implementation as well. You shouldn't be doing things like `return (bool)i;` or `const_cast` in the vast majority of situations either. – AJG85 Dec 27 '11 at 23:15
  • AJG85: Oops, I didn't mean to be using the const iterator in `set_expr_set`. Fixed. Yes, my types, like std::set, have non-const iterators. – Cotton Seed Dec 27 '11 at 23:34
  • Why aren't you using the set in the STL? – piotr Dec 30 '11 at 13:17
  • piotr, set was just to illustrate the problem. My actual application uses different, non-STL datatypes. – Cotton Seed Dec 30 '11 at 15:54

1 Answers1

1

One solution is to make your set expressions copy constructable and assignable (which shouldn't be a big problem if they are relatively light-weight, which seems to be the case). In the set assignment operator, you create a copy of the set expression and iterate over the copy.

template<class T>
set& operator=(const set_expr<T> &e) {
  clear ();
  for (set_expr<T> i = e; i; i++) {
    add_element (i.val());
  }
  return *this;
}

If copying the set expressions turns out to be too expensive, consider moving to c++11 and reading up on move semantics.

masaers
  • 697
  • 9
  • 21
  • Hi Markus, thanks for the reply! This is exactly what I ended up doing, by making the `operator=` argument non-reference. And thanks for the tip on c++11 move semantics, that sounds generally useful. – Cotton Seed Dec 30 '11 at 15:59