Thread overview
Embarrassed to ask this question because it seems so trivial but genuinely curious...
Jan 27, 2022
WhatMeWorry
Jan 27, 2022
Dennis
Jan 27, 2022
H. S. Teoh
January 27, 2022

While studying Ali's book at chapter "Constructor and Other Special Functions" and the below code snippet:

import std.stdio;

struct S {
    this(int i) { writeln("an object"); }

    // Original
    //this(int i) const { writeln("a const object"); }
    //this(int i) immutable { writeln("an immutable object"); }
    //this(int i) shared { writeln("a shared object"); }

    const this(int i) { writeln("a const object"); }
    immutable this(int i) { writeln("an immutable object"); }
    shared this(int i) { writeln("a shared object"); }
}

void main() {
    auto m = S(1);
    auto c = const(S)(2);
    auto i = immutable(S)(3);
    auto s = shared(S)(4);
}

Assuming I can speak in correct programmer-ese: I was wondering why the qualifiers were placed after the function parameter list (int i). Just for fun, I moved to type qualifiers before the function definitions "this" (like a return type?) and the output was exactly identical. So I guess my question is, is this just a matter of esthetics or is some more nuanced goal at work here?

January 27, 2022

On Thursday, 27 January 2022 at 17:42:09 UTC, WhatMeWorry wrote:

>

So I guess my question is, is this just a matter of esthetics or is some more nuanced goal at work here?

It doesn't matter much for constructors, but in general, the problem with placing qualifiers in front is that it looks confusing:

struct S
{
    immutable int[] f()
    {
        return [];
    }
}

This reads as if it returns an immutable(int[]), but it doesn't, the immutable means that it can only be called on immutable instances of S.

January 27, 2022

On 1/27/22 12:42 PM, WhatMeWorry wrote:

>

Assuming I can speak in correct programmer-ese: I was wondering why the qualifiers were placed after the function parameter list (int i).  Just for fun, I moved to type qualifiers before the function definitions "this" (like a return type?) and the output was  exactly identical.  So I guess my question is, is this just a matter of esthetics or is some more nuanced goal at work here?

For constructors, being on the front is not misleading. But for a member function that returns a value, take a look:

struct S
{
   int * x;
   const int *foo() { return x; }
}

What do you think happens here?

The answer, is that this is a compiler error. The error is that the const applies to the this parameter and not the return value. So you are accepting a const S, and trying to return its member x as a plain int *.

This is why we always recommend putting the this modifiers at the end of the function to make it visually clear that they don't apply to the return value.

-Steve

January 27, 2022
On Thu, Jan 27, 2022 at 05:42:09PM +0000, WhatMeWorry via Digitalmars-d-learn wrote:
> While studying Ali's book at chapter "Constructor and Other Special Functions" and the below code snippet:
[...]
>         // Original
>         //this(int i) const { writeln("a const object"); }
>         //this(int i) immutable { writeln("an immutable object"); }
>         //this(int i) shared { writeln("a shared object"); }
> 
>         const this(int i) { writeln("a const object"); }
>         immutable this(int i) { writeln("an immutable object"); }
>         shared this(int i) { writeln("a shared object"); }
[...]
> Assuming I can speak in correct programmer-ese: I was wondering why the qualifiers were placed after the function parameter list (int i). Just for fun, I moved to type qualifiers before the function definitions "this" (like a return type?) and the output was  exactly identical.  So I guess my question is, is this just a matter of esthetics or is some more nuanced goal at work here?

In method declarations, modifiers like const/immutable/shared play two distinct roles:

1) Qualifying the return type of the method;
2) Qualifying the implicit `this` parameter of the method.

Historically, D has been rather lax about where qualifiers in the sense
of (2) can go, so that's why:

	const this()

is the same as

	this() const

They both mean that the implicit `this` parameter (not to be confused with `this` as the name of the ctor) is const.

Personally, though, I prefer the 2nd form, because the first form could potentially be ambiguous in non-ctor member functions:

	struct S {
		const int method();
	}

Does the const apply to `int` or to the implicit `this` parameter? It's not obvious. Better to write it this way:

	int method() const;	// const applied to `this`
	const(int) method();	// const applied to return type

N.B. the parentheses in `const(int)` -- while D is lax in allowing you to write `const int`, that leads to the ambiguous situation above. My personal recommendation is to always parenthesize qualified types so that they are never ambiguous.


T

-- 
Mediocrity has been pushed to extremes.