static operator () and [] (C++23)
Asked Answered
G

1

7

introduced static versions of the operator () and operator []:

#include <iostream>
#include <format>

struct S
{
    static int operator()(int a, int b) { return a + b; }
    static int operator[](int a, int b) { return a - b; }
};

int main()
{
    std::print("({})[{}]", S{}(1, 0), S{}[3, 1]); // (1)[2]
//                          ^^         ^^ <-- Create temporary instances of 'S'?!
    std::print("({})[{}]", S()(2, 1), S()[5, 1]); // (3)[4]
//                          ^^         ^^ <-- Create temporary instances of 'S'?!
    return 0;
}

It shocked me that in order to call the static versions of operators () and [] an instance of the class is required; I was expecting to be able to call both operators from the type (as other static members) but it is ill formed:

//            type --> v        v <-- type
std::print("({})[{}]", S(1, 0), S[3, 1]); // (1)[2]
// static operator() -> \____/   \____/ <- static operator[]

But S(1, 0) looks like a constructor haha, silly me!, it will cause ambiguities if the type S happens to have a S(int, int) constructor… but what about S[3, 1]? This doesn't clash with any in-class entity.

Ok, maybe type(...) and type[...] are problematic but what about using the scope operator? unfortunatelly this is ill formed as well:

//            type --> v          v <-- type
std::print("({})[{}]", S::(1, 0), S::[3, 1]); // (1)[2]
//   static operator() -> \____/     \____/ <- static operator[]

I've been reading the p1169r4 and p2589r0 papers and I wasn't able to find any rationale to "force" to instance the type in order to call the static operator () or [] nor why the type::(...) or type::[...] forms aren't allowed.

Am I missing something?

Guesthouse answered 28/3, 2023 at 16:6 Comment(1)
What you're suggesting might be an interesting addition, but based on the papers, it looks like much of the motivation is for use with lambdas. With a lambda, an instance is easily available, and the type...much less so.Mauritamauritania
C
13

The motivation for static operator() and then, for consistency, static operator[] was to allow making those operators static. To allow s(a, b) to not have to require the extra object parameter and simply hope that it will be optimized out.

But the name of the function is operator() - it's not (). S::(a, b) isn't the way to call that operator today, it's S::operator()(a, b). Allowing this different invocation syntax for operators is a different feature entirely.

The proposals you're citing didn't have rationale to "force" anything because they aren't forcing anything - that's the existing language.

It also doesn't seem like a super motivating feature, since in the cases that you have a static call or static subscript operator, you probably don't have state (otherwise the operators probably wouldn't have been static), so S::(a, b) isn't any shorter than S{}(a, b).

But also if you want to use it that way, you could just create a global object of the type:

struct S
{
    static int operator()(int a, int b) { return a + b; }
    static int operator[](int a, int b) { return a - b; }
};

inline S S;

Which allows you the syntax you originally wanted:

std::print("({})[{}]", S(1, 0), S[3, 1]); // (1)[2]
// static operator() -> \____/   \____/ <- static operator[]
Cohby answered 28/3, 2023 at 16:19 Comment(1)
Well, It's obvious that S::(a, b) isn't any shorter than S{}(a, b) but in the former we're clearly calling something on S scope without instancing S and in the later an instance is created which is weird considering that we're calling a static member. What I mean is that S::(a, b) carries a very different meaning than S{}(a, b).Guesthouse

© 2022 - 2024 — McMap. All rights reserved.