Thread overview
Overloading based on named arguments is possible, here’s how
May 08
cc
May 07

What one might want to do:

struct X
{
    this(int x) { … } // 1
    this(int y) { … } // 2
}

void main()
{
    X(x: 1); // call 1
    X(y: 2); // call 2
}

Of course, that doesn’t work. Parameter names aren’t part of the function signature and therefore, the two constructors clash. It’s not an ambiguity error on the call-site, it’s just that the two constructors have the same mangle.

However, I found a neat trick how it can be done. For each problematic overload, add an enum type with one value:

enum f_x_t { value }
enum f_y_t { value }

Then, change each overload so that it takes a value of the respective enum type as their first parameter, defaulted to the obvious value:

struct X
{
    this(f_x_t = f_x_t.value, int x) { … } // 1
    this(f_y_t = f_y_t.value, int y) { … } // 2
}

Then, the two constructors have different mangles and can be distinguished in the object file. In code, using named arguments, one can re-order the parameters and because the enum parameters are defaulted, they need no arguments.

import std.stdio;

struct X
{
    private enum f_x_t { value }
    private enum f_y_t { value }

    this(f_x_t = f_x_t.value, int x) { writeln("X(int x) called with ", x); }
    this(f_y_t = f_y_t.value, int y) { writeln("X(int y) called with ", y); }
}

void main()
{
    X(x: 1); // X(int x) called with 1
    X(y: 2); // X(int y) called with 2
}

For ordinary functions, the following works as well:

void f_x(int x) { writeln("f(int x) called with ", x); }
void f_y(int y) { writeln("f(int y) called with ", y); }
alias f = f_x;
alias f = f_y;

The only downside is that it does not work for constructors, possibly among others.

May 08

On Tuesday, 7 May 2024 at 11:58:59 UTC, Quirin Schroll wrote:

>

Then, the two constructors have different mangles and can be distinguished in the object file. In code, using named arguments, one can re-order the parameters and because the enum parameters are defaulted, they need no arguments.

Interesting! Looks like this can be done via template arguments as well.

struct X {
	this(string f = "x")(int x) { writeln("X(int x) called with ", x); }
	this(string f = "y")(int y) { writeln("X(int y) called with ", y); }
}
X(x: 1); // X(int x) called with 1
X(y: 2); // X(int y) called with 2
May 08

On Wednesday, 8 May 2024 at 04:46:26 UTC, cc wrote:
[...]

>

X(x: 1); // X(int x) called with 1
X(y: 2); // X(int y) called with 2

Cool!