Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

An issue about the member function qualified by volatile qualifier

#include <iostream>
struct A{
    A() = default;
    A(volatile const A&){}
    void show()const volatile {

    }
};
int main(){
   volatile A a;
   //A b = std::move(a);  // ill-formed
   std::move(a).show();  //OK
}

Consider the example, the results of the example are out of my understanding about some relevant rules.

For A b = std::move(a);, it's ill-formed, because it violates the following rule, that is:
dcl.init.ref#5.2

Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed.

That means, a lvalue reference to const volatile-qualified T cannot bind to any rvalue even though they're reference-compatible. A b = std::move(a); obviously violates this rule, hence it's ill-formed.

However I don't know why compile std::move(a).show();without reporting wrong. According to this rule:

For non-static member functions, the type of the implicit object parameter is

“lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier

The type of the implicit object parameter of member function show will be volatile const A& . In general, it definitely violates [dcl.init.ref#5.2]. If change the definition of member function show to:

void show() volatile const& {

}

std::move(a).show(); will be ill-formed. So must be some magic in the following rule that make std::move(a).show(); be compiled before changing show. The rule is:
over.match.funcs#general-5

For non-static member functions declared without a ref-qualifier, an additional rule applies:

even if the implicit object parameter is not const-qualified, an rvalue can be bound to the parameter as long as in all other respects the argument can be converted to the type of the implicit object parameter.

Honestly, I really don't know what does the wording "in all other respects" mean? And what does the "the type of the implicit object parameter" refer to? Does the "type" refer to volatile const A& or the referenced type volatile const A? The wording is very vague. Anyhow, lvalue reference to const volatile T cannot bind to any rvalue of type T. So, how to interpret that?

As a contrast:

#include <iostream>
struct B{
  void show(){}
};
int main(){
  volatile B b;
  std::move(b).show();  //ill-formed
}

the type of the implicit object parameter of show would be B&, According to [over.match.funcs#general-5], even though ignore const-qualifier, it's still ill-formed due to it discards the volatile-qualifier. From this example, it implies that, For this sentence "in all other respects the argument can be converted to the type of the implicit object parameter", where the type should refer to reference type rather than the type the reference refers to. If the magic is this, it's still not sufficient to make std::move(a).show(); to be well-formed.

So, how to interpret these issues? I don't know how to use [over.match.funcs#general-5] to interpret these two examples.

like image 294
xmh0511 Avatar asked Nov 11 '20 11:11

xmh0511


People also ask

What is volatile qualifier in C++?

The volatile qualifier declares a data object that can have its value changed in ways outside the control or detection of the compiler (such as a variable updated by the system clock or by another program).

Can functions be volatile?

Volatile functions are functions in which the value changes each time the cell is calculated. The value can change even if none of the function's arguments change. These functions recalculate every time Excel recalculates. For example, imagine a cell that calls the function NOW .

Can we use const and volatile together?

Yes a C++ variable be both const and volatile. It is used in situations like a read-only hardware register, or an output of another thread. Volatile means it may be changed by something external to the current thread and Const means that you do not write to it (in that program that is using the const declaration).

What is the use of const volatile in C?

The const keyword specifies that the pointer cannot be modified after initialization; the pointer is protected from modification thereafter. The volatile keyword specifies that the value associated with the name that follows can be modified by actions other than those in the user application.


1 Answers

struct A {
    A() = default;
    A(volatile const A &) {}
    void show() const volatile {}
};

int main() {
    volatile A a;
    std::move(a).show(); // OK
}

The implied object parameter for the member function show() is, as per [over.match.funcs]/4, const volatile A&, such that for the purpose of overload resolution, we may, as per [over.match.funcs]/5, consider the data member function as

void show(const volatile A&);

Now, with this in mind, let's first simplify the example, with the purpose of:

  • Comparing why an rvalue reference of A seemingly may bind to an implied object parameter or type const volatile A& but not to say a function parameter of the same type when the parameter is for a regular free function.

Thus, consider the following simplified example:

#include <memory>

struct A {
    void show() const volatile {}
};

void g(const volatile A &) { }

int main() {
    volatile A a;
    
    g(std::move(a));      // (i) Error.
    std::move(a).show();  // (ii) OK.
}

The error message at (i), in GCC (10.1.0) is:

error: cannot bind non-const lvalue reference of type const volatile A& to an rvalue of type std::remove_reference<volatile A&>::type {aka volatile A}

which is expected (as you have noted yourself) as per [dcl.init.ref]/5.2, which disallows rvalues to bind, in initialization, to volatile references, even if they are const-qualified.

Then why is (ii) accepted? Or, conversely, why does the restriction of [dcl.init.ref]/5.2 apparently not apply for the similar case of the implicit object parameter of a member function?

The answer lies in [over.match.funcs ]/5.1, which contains an exception for member functions declared without a ref-qualifier:

[over.match.funcs ]/5 [...] For non-static member functions declared without a ref-qualifier, an additional rule applies:

  • /5.1 even if the implicit object parameter is not const-qualified, an rvalue can be bound to the parameter as long as in all other respects the argument can be converted to the type of the implicit object parameter.

[over.match.funcs ]/5.1 removes the prohibation of [dcl.init.ref]/5 regarding rvalues (rvalue binding), and the remaining criteria is applied to whether argument (rvalue ignored; volatile A) can be ("in all other respects") converted to the implicit object parameter (const volatile A&). As the implicit object parameter, as shown above, in this case is always an lvalue reference, "in all other respects" here essentially means that the implicit object parameter is reference-compatible (as per [dcl.init.ref]/4) with the (rvalue ignored) argument type.

// [over.match.funcs ]/5.1 special case: rvalue prohibition waived
   volatile A a;                // argument: a
   const volatile A& aref = a;  // ok, reference-compatible
// ^^^^^^^^^^^^^^^^^ implicit object parameter

Arguably [over.match.funcs]/5.1 could be clearer on that it applies both for the case where non-const qualification (usually) prohibits from-rvalue binding, and that volatile-cv-qualification (usually) prohibits from-rvalue binding.


We may finally query the compiler(s) whether this is actually the particular rule which it uses to allow (ii), by explicitly adding the &-ref-qualifier, a change that as per [over.match.funcs]/4.1 will have no effect on the type of the implicit object parameter:

#include <memory>

struct A {
    void show() const volatile & {}
};

void g(const volatile A &) { }

int main() {
    volatile A a;
    
    g(std::move(a));      // (i) Error.
    std::move(a).show();  // (ii') Error.
}

As expected, if we add a &-qualifier to the show() overload, (ii) likewise fails as (i) did, albeit with another error message (GCC):

error: passing std::remove_reference<volatile A&>::type {aka volatile A} as this argument discards qualifiers

For this error, Clang (10.0.0) arguably has a more on-spot error message:

error: this argument to member function show is an rvalue, but function has non-const lvalue ref-qualifier

like image 176
dfrib Avatar answered Oct 16 '22 21:10

dfrib