Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[conv.integral,over.best.ics.general,over.ics.user] Ignore cv-qualification differences #4860

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

jensmaurer
Copy link
Member

Fixes #4851

@xmh0511
Copy link
Contributor

xmh0511 commented Sep 3, 2021

In terms of the opportunity to clarify these concepts, consider this example

void fun(int&){}  // #1
void fun(int){}   // #2
int a = 0;
fun(a);

The identity conversion is vague here. Since using a glvalue a to initialized the parameter at #2 with type int, the conversion comprises "lvalue-to-rvalue conversion" as per the order defined in [conv.general] p1, now the result is a prvalue of type int, since the definition of [conv.qual], from a prvalue of type int to a prvalue of type int is qualification conversion. Instead, for #1, we explicitly say it is an identity conversion in [over.ics.ref] p1. Hence, as per [over.ics.rank#3.2.1], #1 is the best viable function? Actually, the implementations report ambiguity.

This issue arises from when we step into [conv#general-1.4]

Zero or one qualification conversion.

How should we treat them where whether, from a prvalue of type T to the same type, is necessary to be considered qualification conversion?

Back to the definition of identity conversion, the relevant notes and normative rules are
[over.ics.scs#2]

As described in [conv], a standard conversion sequence either is the Identity conversion by itself (that is, no conversion) or consists of one to three conversions from the other four categories.

[conv#general-note-1]

A standard conversion sequence can be empty, i.e., it can consist of no conversions.

[over.best.ics#general-8]

If no conversions are required to match an argument to a parameter type, the implicit conversion sequence is the standard conversion sequence consisting of the identity conversion ([over.ics.scs]).

Does it mean, even though an lvalue-to-rvalue conversion applies and the result prvalue with the same type as that of the target, it cannot be considered as identity conversion? Moreover, from a prvalue of type T to the same type, it can also be considered as qualification conversion, so it is also not identity conversion?


Clarification, I think we should give a terminal condition of implicit conversion(i.e., standard implicit conversion) to avoid the endless conversion that is permitted on concept or definition.

int a =0;
int const volatile t = a; // just lvalue-to-rvalue conversion

Even though we say a standard conversion sequence can consist of one to three conversions from the other four categories, and a qualification conversion is permitted by the concept. In other words, we should give a condition to avoid it go into [conv#general-1.4] if that condition is satisfied.

@jensmaurer
Copy link
Member Author

I think the intent is that all conversions described in [conv] are not "empty".

This is clear in the formulation of integral and floating-point conversions, where we say "another ... type". Which didn't properly exclude mere differences in cv-qualification (first patch in this pull request). Similarly, [conv.qual] should only allow conversions to a different type, excluding a conversion from T to T (second patch in this pull request).

@xmh0511
Copy link
Contributor

xmh0511 commented Sep 4, 2021

Please still consider [conv.qual], give such two example

const volatile int v = 0;  // #1
const volatile Class obj = Class();  // #2

The target type of #1 and #2 are all cv-qualified types. Consider #1, as per [dcl.init#general-15.9], a standard conversion sequence will be used if necessary, although the fixed [conv.integral] has rejected to be used, however, [conv.qual] still permit to be used if they are different types. Is there necessary to convert int to const volatile int by a qualification conversion? Presumably, it's not necessary, #2 is also similar. In these cases, a prvalue of cv1 T is sufficient to initialize the object of cv2 T, a qualification conversion is not necessary here. If it would be permitted, it will impact [over.rank]. More generally, I think [conv.qual] is meaningful to be used only in the case that the cv-qualifiers other than cv0 of two similar types are different.

@jensmaurer
Copy link
Member Author

jensmaurer commented Sep 4, 2021

I think there are situations where the need for a top-level qualification-conversion is significant:

int f(int &x);
int f(const int& x);
int a;
int y = f(a);  // selects f(int&)

Note also that forming a conversion sequence in overload resolution is different from the actual initialization in [dcl.init]. For the actual initialization, [dcl.init] should (does it?) ignore top-level cv-qualifiers on the target.

@xmh0511
Copy link
Contributor

xmh0511 commented Sep 4, 2021

IMO, regardless of the first or second overload, the parameters are all of the reference type, a valid reference type cannot be cv-qualified, in this case, the rank is impacted by [over.ics.rank#3.2.6], that rule explicitly says we talk about the top-level cv-qualifiers of the referenced type. I think the qualification conversion is almost prepared for pointer types and their top-level cv-qualifiers do not have an effect.

int* ptr = 0;
int* const ptr1 = ptr // lvalue-to-rvalue conversion is sufficient

int const* ptr2 = ptr // lvalue-to-rvalue conversion followed by a qualification conversion

Hence, we can generally say qualification conversion is not necessary to apply to two similar types where their difference only in cv0(i.e. the top-level cv-qualification of that type).


[dcl.init] should (does it?) ignore top-level cv-qualifiers on the target.

I'm not clear about whether the initialization is or not, however, for overload resolution, the top-level cv-qualifiers are ignored once the function type has formed. In the initialization case, the target type is defined as.
[dcl.init#general-15]

The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the source type is not defined.

It could arguably say the top-level cv-qualifier is considered in the destination type.

@tkoeppe tkoeppe added the cwg Issue must be reviewed by CWG. label Sep 25, 2021
@@ -780,7 +780,7 @@
where $\cv{}^j_i$ and $P^j_i$ are the components of
the qualification-decomposition of $\tcode{T}j$.
A prvalue of type \tcode{T1}
can be converted to type \tcode{T2}
can be converted to another type \tcode{T2}
Copy link
Contributor

@xmh0511 xmh0511 Jul 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we have excluded the identity conversion from qualification conversion, [expr.const.cast] p7 should also have to be changed to adhere this modification

[expr.const.cast] p7 says

A conversion from a type T1 to a type T2 casts away constness if T1 and T2 are different, there is a qualification-decomposition ([conv.qual]) of T1 yielding n such that T2 has a qualification-decomposition of the form

  • [...]

and there is no qualification conversion that converts T1 to

  • [...] named T3

Consider this example:

int* ptr = 0;
static_cast<void*>(ptr); 

In this example, T1 is "pointer to int" while T2 is "pointer to void". When n=1, T3 is pointer to void, since T2 and T3 are the same types, hence there is no qualification conversion that converts T2 to T3([conv.qual] p3 excludes identify conversions). This is not the intent of the definition of the cast-away constness.

[expr.const.cast] p7 should be changed to

A conversion from a type T1 to a type T2 casts away constness if T1 and T2 are different, there is a qualification-decomposition ([conv.qual]) of T1 with the form

cv10 P10 cv11 P11 ... cv1n-1 P2n-1 cv1n U1

yielding n such that T2 has a qualification-decomposition of the form

  • cv20 P20 cv21 P21 ... cv2n-1 P2n-1 cv2n U2

and such that a type T3 has a qualification-decomposition of the form

  • cv20 P10 cv21 P11 ... cv2n-1 P1n-1 cv2n U1

there is no qualification conversion that converts T1 to T3 if they are different types

Option 2

Or we just say: The qualification-combined type of T1 and T3 is not T3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cwg Issue must be reviewed by CWG. needs rebase The pull request needs a git rebase to resolve merge conflicts.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[conv.qual] identity conversion and similar type
4 participants