0

I'm trying to make some nice syntax for assignment of array slices using operator overloading. Due to some peculiarities with const-correctness of references, different classes are needed for lvalue and rvalue slices. However, I cannot get the compiler to understand which version to use in a given context.

The code below shows a simplified version of the original code, and while it looks a bit contrived, it captures the issue fairly well. In the real code, i is a whole set of indices, and B/ConstB represents slices of huge arrays that cannot be copied around, motivating the reference x.

The problem occurs when I want to do the assignment in foo. I want all slices on the right to yield a ConstB object, but since a1 happens to be non-const in this case, the wrong one is used. I can enforce it by introducing a const_cast, but I would prefer some way that does not destroy the user code (i.e. the thing inside foo). Is there a way to enforce that a given overload is called for lvalues or rvalues, respectively?

The code:

struct B;
struct ConstB;

struct A
{
  double data[10];

  A() {}
  A&     operator=(const ConstB &b);
  ConstB operator()(int i) const;
  B      operator()(int i);
};


struct B
{
  double &x;

  B(double *d, int i);
  B& operator=(const A &a);
};

struct ConstB
{
  const double x;

  ConstB(const double *d, int i);
};



A& A::operator=(const ConstB &b) {
  data[0] = b.x;
  return *this;
}

ConstB A::operator()(int i) const {
  return ConstB(data,i);
}

B A::operator()(int i) {
  return B(data,i);
}


B::B(double *d, int i)
  : x(d[i]) {}

B& B::operator=(const A &a) {
  x=a.data[0];
  return *this;
}

ConstB::ConstB(const double *d, int i)
  : x(d[i]) {}



void foo(A &a1, A &a2, A&a3)
{
  a1(2) = a2;

  a3 = a1(3);
  // a3 = const_cast<const A&>(a1)(3);
}

int main(int argc, char *argv[])
{
  A a1;
  A a2;
  A a3;

  foo(a1,a2,a3);

  return 0;
}

Resulting error message:

test.cc: In function ‘void foo(A&, A&, A&)’:
test.cc:67:6: error: no match for ‘operator=’ (operand types are ‘A’ and ‘B’)
   a3 = a1(3);
      ^
test.cc:36:4: note: candidate: A& A::operator=(const ConstB&)
 A& A::operator=(const ConstB &b) {
    ^
test.cc:36:4: note:   no known conversion for argument 1 from ‘B’ to ‘const ConstB&’
test.cc:5:8: note: candidate: A& A::operator=(const A&)
 struct A
        ^
test.cc:5:8: note:   no known conversion for argument 1 from ‘B’ to ‘const A&’
test.cc:5:8: note: candidate: A& A::operator=(A&&)
test.cc:5:8: note:   no known conversion for argument 1 from ‘B’ to ‘A&&’
kalj
  • 1,432
  • 2
  • 13
  • 30
  • Why not add an overload for the assignment operator to take a `B`? – NathanOliver Jan 18 '17 at 13:59
  • @NathanOliver Because I am hoping to maintain const-correctness for `x` which in the real code is a large vector. Having a const reference instead of just a reference seems like a good thing for compiler optimizations. – kalj Jan 18 '17 at 14:04

0 Answers0