Arlen
Arlen

Reputation: 6835

Should a const member function return a const or non-const?

struct Type
{
  auto opBinary(string op)(Type other) const {
    return Type();         // #1 return type is Type
    return typeof(this)(); // #2 return type is const(Type)
  }
}

unittest
{
  Type t1, t2;
  auto t3 = t1 + t2;
}

In t1.opBinary!("+")(t2), t1 becomes a const, while t2 stays non-const. Should opBinary's return type be Type or const(Type), and why?

const(T) is a supertype, so maybe it should return a const, but I've hardly seen this in practice. Things also become rather complicated when dealing with a hierarchy of types and functions that use those types or are used by those types.

Upvotes: 0

Views: 139

Answers (2)

Mihails Strasuns
Mihails Strasuns

Reputation: 3803

Should return type be T or const(T)? Any will do.

Which one is better? Depends on your intended behavior.

const qualifier on opBinary only means that hidden "this" argument is const. Nothing more, nothing else. It does not imply anything about the return type. It all boils down to pretty simple choice:

struct Type
{
    int a;
    auto opBinary(string op)(Type other) const
           if (op == "+")
    {
        return Type(this.a + other.a);
    }
}

void main()
{
    Type first, second;
    (first + second).a = 42; // Do you want to allow this?
                             // Yes -> return non-const
                             // No -> return const
}

If you want to preserve qualifiers of arguments, use either inout (see Adams answer) or manual check for qualifiers for more complicated choices.

With either choice remember automatic type deduction:

auto third = first + second;
third.a = 42; // Error if returns const

Type fourth = first + second;
fourth.a  = 42; // Fine in both cases, struct was copied

In the end it is about you intention as a type designer how class/struct should behave.

Upvotes: 1

Adam D. Ruppe
Adam D. Ruppe

Reputation: 25595

Since the return value here is a new object, I'd say make it non-const. Being new, it can be modified safely, so no reason to unnecessarily restrict it with const.

If you were returning a part of the existing object, you'll want to use inout instead of const. inout means the constness of the object will also go to the return value.

inout(Type) opBinary(string op)(Type other) inout {
    return whatever;
}

Now if you use a const(Type) object, the return will be const as well, and if it is called on a mutable object, the return is also mutable.

Upvotes: 4

Related Questions