C++ Logo

STD-PROPOSALS

Advanced search

Subject: Re: [std-proposals] proposal: new const-able feature ( use "const?" , or new "constable" keyword) for method/function
From: jianping z (zjpwork_at_[hidden])
Date: 2020-01-17 21:54:40


thank you Bo, that's a very good reminder.

after a second thought, for ref-qualifiers, as currently only & and &&
can be paired for member functions, I think &&? is enough (so we do not
need &? and &&??).

also, as we know we have 4 qualifiers "const", "volatile", "&", "&&",
which we usually call them cv-qualifiers and ref-qualifiers, though
"volatile" is not commonly used, I think the ? qualifier postfix can
also be applied to it, so we have
1) const?    -> const, or ()
2) volatile? -> volatile, or ()
3) &&?       -> &&, or &

with existing type traits like is_const(), is_lvalue_reference(),
is_rvalue_reference(), is_volatile() etc, we can make better use of ?
qualifier postfix to write more versatile member functions.

here, let me demonstrate it first with a simple example.

//example-1a (class written without ? qualifier postfix)
class A
{
public:
 Â  void f() &        { std::cout << "lvalue" << std::endl; }
 Â  void f() &&       { std::cout << "rvalue" << std::endl; }
 Â  void f() const &  { std::cout << "const lvalue" << std::endl; }
 Â  void f() const && { std::cout << "const rvalue" << std::endl; }
};

int main()
{
 Â  A a1;
 Â  a1.f();            // lvalue
 Â  std::move(a1).f(); // rvalue

 Â  const A a2 = A();
 Â  a2.f();            // const lvalue
 Â  std::move(a2).f(); // const rvalue
}

by using type traits and ? qualifier postfix, we can rewrite member
function f() only once for same output.

//example-1b (class rewritten with ? qualifier postfix)
class A
{
public:
 Â  void f() const? &&?  { std::cout << (std::is_const(const? int) ?
"const " : "") << (std::is_lvalue_reference(int &&?) ? "lvalue" :
"rvalue") << std::endl; }
}

based on example-2, the auto generated class A by compiler should be

//auto generated class for example-1b (before code trim from compiler)
class A
{
public:
 Â  void f() &        { std::cout << (std::is_const(int) ? "const " : "")
<< (std::is_lvalue_reference(int &) ? "lvalue" : "rvalue") << std::endl; }
 Â  void f() &&       { std::cout << (std::is_const(int) ? "const " : "")
<< (std::is_lvalue_reference(int &&) ? "lvalue" : "rvalue") << std::endl; }
 Â  void f() const &  { std::cout << (std::is_const(const int) ? "const "
: "") << (std::is_lvalue_reference(int &) ? "lvalue" : "rvalue") <<
std::endl; }
 Â  void f() const && { std::cout << (std::is_const(const int) ? "const "
: "") << (std::is_lvalue_reference(int &&) ? "lvalue" : "rvalue") <<
std::endl; }
}

the auto generated class works almost the same as the class A in
example-1a, though it looks verbose for most of us.

we know type traits can be evalutated at compile time, if compiler
support to trim code at compile time for "expr1 ? expr2 : expr3" to only
"expr2" or "expr3" depending on the value of "expr1" evaluated at
compile time, the code will look better and more similar

//auto generated class for example-1b (after code trim from compiler)
class A
{
public:
 Â  void f() &        { std::cout << "" << "lvalue" << std::endl; }
 Â  void f() &&       { std::cout << "" << "rvalue" << std::endl; }
 Â  void f() const &  { std::cout << "const " << "lvalue" << std::endl; }
 Â  void f() const && { std::cout << "const " << "rvalue" << std::endl; }
}

as the example demonstrates, if compiler support compile time code trim,
the final generated code could be almost as good as the one we written
in example-1a.

what we wish for compile time code trim
1. for "expr1 ? expr2 : expr3", if "expr1" can be evaluated at compile
time, compiler can trim it into "expr2" or "expr3" depending on value of
"expr1"
2. for "if(expr1) statment2; else statement3;", if "expr1" can be
evaluated at compile time, compiler can trim it to "statement2;" or
"statement3;" depending on the value of "expr1"

it may seem to be not needed for the above example as developer won't
see the final generated class, only the compiler will process code of
the final generated class. but it can make difference to next example
with ref-qualifiers on return value as mentioned by Barry in the earlier
thread.

//example-2a
template <typename T>
class optional {
public:
 Â auto operator*() &       -> T&        { return value; }
 Â auto operator*() const&  -> T const&  { return value; }
 Â auto operator*() &&      -> T&&       { return std::move(value); }
 Â auto operator*() const&& -> T const&& { return std::move(value); }
};

we may rewrite it with ? qualifier postfix and type traits

//example-2b (class rewritten with ? qualifier postfix)
template <typename T>
class optional {
public:
 Â  auto operator*() const? &&? -> T const? &&? { return
is_lvalue_reference(int &&?) ? value : std::move(value); }
};

without compile time code trim, compiler may complain the mismatch
return value types of "value" and "std::move(value)". with complie time
code trim, the compiler will not complain after auto generated member
functions are trimmed, and the final generated code will look exactly
the same as the class in example-2a

with compile time code trim support, we can use ? qualifier postfix and
type traits to rewrite most cv-qualifiers, and ref-qualifier overloaded
member functions.

Thanks,
Jianping

On 12/18/2019 02:25 AM, Bo Persson via Std-Proposals wrote:
> On 2019-12-17 at 22:20, jianping z via Std-Proposals wrote:
>> Thank you and Michael Hava for the updates, it turns out my C++
>> knowledge needs to be updated to C++17.
>>
>> Here are some of my thoughts about your concern,
>>
>> 1. the "&&?" notation
>> we may treat "&&" as a token having two "&", and each "?" can
>> optionally remove one "&",
>> following the rule, "&&??" can be used to optionally remove two "&"
>> from "&&" with "??".
>>
>> a) const?    -> (), const   //2 variations with single "?"
>> b) &?        -> (), &       //2 variations with single "?"
>> c) &&?       -> &, &&       //2 variations with single "?"
>> d) &&??      -> (), &, &&   //3 variations with double "?" ("??")
>>
>> here, left side is the notations, right side are the variations of
>> the final tokens to replace the notation, "()" means no token
>> (notation removed)
>> by explaining "?" like this, developer may able to accept this
>> concept easily. though "&?", and "&&??" are hardly needed, but they
>> may still be usefully, for example
>>
>
> Readability concerns aside, using double question marks gets you very
> close to accidentally stumble into trigraphs. Although now formally
> removed from C++, several compilers still support them as an extension.
>
> For example, see "What does the C ??!??! operator do?"
>
> https://stackoverflow.com/questions/7825055/what-does-the-c-operator-do
>
>
>    Bo Persson
>


STD-PROPOSALS list run by std-proposals-owner@lists.isocpp.org

Standard Proposals Archives on Google Groups