Check the viability of a conversion function [duplicate] - c++

This question already has answers here:
Why can a member function be called on a temporary but a global function cannot?
(2 answers)
Closed 6 months ago.
I have the following code:
struct S {
operator int(); // F1
operator double(); // F2
};
int main() {
int res = S();
}
Since neither F1 nor F2 is cv-qualified, the type of the implicit object parameter is S&, and the corresponding argument to be matched against is S().
Now, per [over.match.viable]/4: (emphasis mine)
Third, for F to be a viable function, there shall exist for each
argument an implicit conversion sequence that converts that argument
to the corresponding parameter of F. If the parameter has reference
type, the implicit conversion sequence includes the operation of
binding the reference, and the fact that an lvalue reference to
non-const cannot bind to an rvalue and that an rvalue reference cannot
bind to an lvalue can affect the viability of the function (see
[over.ics.ref]).
According to the above quote (bold), I'm expecting that neither F1 nor F2 is viable, because the implicit object parameter for both is of type S& and it cannot bind to an rvalue, S().
But, when I tried to compile the code, I found out both functions are viable candidates. Which is best match is not what I am asking about here.
So, why are both F1 and F2 viable candidates, even though the implicit object parameter (of type S&) cannot bind to class prvalue S()?

The main concern expressed in your question appears to be how a given rvalue argument can bind to an implicitly declared lvalue reference parameter. (I'm not here even attempting to make an adjudication on the extensive discusssion in the comments to your question about whether or not any actual overloads are involved in your code sample.)
This (main) concern is addressed – quite clearly, IMHO – in another part of the [over.match.funcs] section of the C++ Standard you cite (bold emphasis mine):
12.2.2.1       General[over.match.funcs.general]
…
5     During overload resolution, the implied
object argument is indistinguishable from other arguments. The implicit
object parameter, however, retains its identity since no user-defined
conversions can be applied to achieve a type match with it. For
implicit object member functions declared without a ref-qualifier,
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.
Without this paragraph, implicit conversion functions would lose a great deal of their usefulness, such as the use-case you have provided in your example.

Related

Contradicting definition of implicit this parameter in the standard

I am learning about classes in C++. I came across the following statement from the standard:
During overload resolution, non-static cv-qualified member function of class X is treated as a function that takes an implicit parameter of type lvalue reference to cv-qualified X if it has no ref-qualifiers or if it has the lvalue ref-qualifier. Otherwise (if it has rvalue ref-qualifier), it is treated as a function taking an implicit parameter of type rvalue reference to cv-qualified X.
The above statement seems to imply that for a const qualified non-static member function of a class X will have an implicit parameter of type const X&.
But then i also came across:
The type of this in a member function of class X is X* (pointer to X). If the member function is cv-qualified, the type of this is cv X* (pointer to identically cv-qualified X). Since constructors and destructors cannot be cv-qualified, the type of this in them is always X*, even when constructing or destroying a const object.
So according to the above second quote the implicit this parameter for a const qualified non-static member function of class Xhas type const X*.
My question is that why is there such a difference. I mean during overload resolution for a const qualfied nonstatic member function, why is the implicit parameter considered as a const X& and not simply a const X* which seems to be the actual type of this.
The implicit object parameter is not the same as this. this is a pointer referring to the object on which the member function was called while the implicit object parameter is the imagined first parameter of the member function, which is passed the object expression in the member function call (whats left of . in the member access expression) and so should be a reference parameter.
It wouldn't make sense to use a pointer for the implicit object parameter. It would make it impossible to overload the function on value category of the object expression. If a member function is &&-qualified, the implicit object parameter is a rvalue reference, so that if the object expression is a rvalue overload resolution correctly overload with a &-qualified member function.
So the implicit object parameter is const T& in your example, but this has type const T*. There is no contradiction.

Should initialization by conversion function be ambiguous when two candidates have the same cv-qualification?

Both clang and gcc accept the following code and choose A::operator B*.
struct B
{
};
struct A : B
{
operator A*();
operator B*();
};
A a;
void* x = a;
My reading of the standard - specifically sentences highlighted below in bold - suggests that this conversion should be ambiguous.
Both A::operator A* and A::operator B* are candidates for overload resolution because A* and B* are both convertible to void* via a standard conversion. Because the implied object parameter A& is the only argument, only the conversion sequence that converts from the implied object argument to the implied object parameter is considered - the type yielded by the conversion function is ignored. In both cases, the implied object argument is the initializer expression's type A, and the implied object parameter is A&. If both conversion sequences are identical, there is no way to distinguish between the two candidates.
8.5 Initializers [dcl.init]
The semantics of initializers are as follows. 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 destination type is a [reference/array/class...] [deleted details not applicable to this scenario]
— Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered.
The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload
resolution (13.3). The user-defined conversion so selected is called to convert the initializer
expression into the object being initialized. If the conversion cannot be done or is ambiguous, the
initialization is ill-formed.
13.3.1.5 Initialization by conversion function [over.match.conv]
Under the conditions specified in 8.5, as part of an initialization of an object of nonclass type, a conversion
function can be invoked to convert an initializer expression of class type to the type of the object being
initialized. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1
T” is the type of the object being initialized, and “cv S” is the type of the initializer expression, with S a
class type, the candidate functions are selected as follows:
— The conversion functions of S and its base classes are considered. Those non-explicit conversion
functions that are not hidden within S and yield type T or a type that can be converted to type T
via a standard conversion sequence (13.3.3.1.1) are candidate functions. For direct-initialization, those
explicit conversion functions that are not hidden within S and yield type T or a type that can be
converted to type T with a qualification conversion (4.4) are also candidate functions. Conversion
functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type
for this process of selecting candidate functions. Conversion functions that return “reference to cv2
X” return lvalues or xvalues, depending on the type of reference, of type “cv2 X” and are therefore
considered to yield X for this process of selecting candidate functions.
The argument list has one argument, which is the initializer expression. [ Note: This argument will be
compared against the implicit object parameter of the conversion functions. —end note ]
Is this ambiguous according to the standard?
EDIT: note that this is a similar question, but not the same as Distinguishing between user-defined conversion sequences by the initial standard conversion sequence
The difference being that in my example both conversion functions have the same qualification.
TLDR: When everything else is equal, overload resolution breaks the tie by which conversion function has the best conversion from its return value to the target type.
All references are to ISO/IEC 14882:2011 (C++11). The behavior of the initialization:
void* x = a;
is defined as follows. First, this is an initialization as described in 8.5 Initializers [dcl.init] and conforms to the grammar described in p1. Since the destination type void* is a non-class type, and the source type A is a class type, this specific initializer is of the form described in 8.5 p16, bullet 7:
Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload resolution (13.3). The user-defined conversion so selected is called to convert the initializer expression into the object being initialized. If the conversion cannot be done or is ambiguous, the initialization is ill-formed.
The "enumeration of applicable conversion functions" is detailed in 13.3.1.5 p1:
Under the conditions specified in 8.5, as part of an initialization of an object of nonclass type, a conversion
function can be invoked to convert an initializer expression of class type to the type of the object being
initialized. Overload resolution is used to select the conversion function to be invoked. Assuming that “cv1
T” is the type of the object being initialized, and “cv S” is the type of the initializer expression, with S a
class type, the candidate functions are selected as follows:
The conversion functions of S and its base classes are considered. Those non-explicit conversion
functions that are not hidden within S and yield type T or a type that can be converted to type T
via a standard conversion sequence (13.3.3.1.1) are candidate functions. For direct-initialization, those
explicit conversion functions that are not hidden within S and yield type T or a type that can be
converted to type T with a qualification conversion (4.4) are also candidate functions. Conversion
functions that return a cv-qualified type are considered to yield the cv-unqualified version of that type
for this process of selecting candidate functions. Conversion functions that return “reference to cv2
X” return lvalues or xvalues, depending on the type of reference, of type “cv2 X” and are therefore
considered to yield X for this process of selecting candidate functions.
Note that both A::operator A*() and A::operator B*() are candidate functions, since both A* and B* are convertible to void* per
4.10p2: "A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer
to cv void”." Given that both functions are candidates, overload resolution must choose between them.
Overload resolution is decribed in 13.3 [over.match]. p2 states:
Overload resolution selects the function to call in seven distinct contexts within the language:
...
invocation of a conversion function for initialization of an object of a nonclass type from an expression of class type (13.3.1.5)
...
Each of these contexts defines the set of candidate functions and the list of arguments in its own unique way. But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases:
First, a subset of the candidate functions (those that have the proper number of arguments and meet certain other conditions) is selected to form a set of viable functions (13.3.2).
Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.
Which of our two functions are viable? 13.3.2 [over.match.viable] p1:
From the set of candidate functions constructed for a given context (13.3.1), a set of viable functions is
chosen, from which the best function will be selected by comparing argument conversion sequences for the
best fit (13.3.3).
The requirements are presented in p2:
First, to be a viable function, a candidate function shall have enough parameters to agree in number with
the arguments in the list.
and p3:
Second, for F to be a viable function, there shall exist for each argument an implicit conversion sequence
(13.3.3.1) that converts that argument to the corresponding parameter of F.
Both requirements are trivially met by our conversion functions: they have a single (implicit) argument of the same type as the initializer expression a.
Determination of the best of the viable functions is described in 13.3.3 [over.match.best]. It defines some formalisms for describing conversion sequences, sequences of operations necessary to convert from the types of the actual function arguments to the types of the formal function parameters. In the case of our conversion functions, they both have exactly one parameter whose type is exactly that of the actual argument, so the "conversion sequence" corresponding to each overload is the identity sequence. They are discriminated by the language in p1:
Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the
standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the
entity being initialized) is a better conversion sequence than the standard conversion sequence from
the return type of F2 to the destination type.
What about that final bullet? Does one of our overloads have a better standard conversion sequence from its return type to void*?
13.3.3.2 Ranking Implicit Conversion Sequences [over.ics.rank] p4 states in the second bullet point:
If class B is derived directly or indirectly from class A, conversion of B* to A* is better than conversion of B* to void*, and conversion of A* to void* is better than conversion of B* to void*.
This is exactly the case of the OP, except with the names A and B reversed. Overload resolution on the two conversion operators of the OP is resolved in favor of A::operator B*() since the cited rule makes the conversion sequence B* → void* better than A* → void*

Explicit conversion functions, direct-initialization, and converting constructors

Post-standard draft n3376 has as an example (12.3.2:2) of the use of an explicit conversion function to a user-defined type:
class Y { };
struct Z {
explicit operator Y() const;
};
void h(Z z) {
Y y1(z); // OK: direct-initialization
}
Per 12.3.2:2, an explicit conversion function is "only considered as a user-defined conversion for direct-initialization"; however, that would appear to permit:
struct Y { Y(int); };
struct Z {
explicit operator int() const;
};
void h(Z z) {
Y y1(z); // direct-initialization
}
which appears to conflict with the intent of the standard, and indeed is rejected by gcc-4.7.1:
source.cpp: In function 'void h(Z)':
source.cpp:4:9: error: no matching function for call to 'Y::Y(Z&)'
source.cpp:4:9: note: candidates are:
source.cpp:1:12: note: Y::Y(int)
source.cpp:1:12: note: no known conversion for argument 1 from 'Z' to 'int'
source.cpp:1:8: note: constexpr Y::Y(const Y&)
source.cpp:1:8: note: no known conversion for argument 1 from 'Z' to 'const Y&'
source.cpp:1:8: note: constexpr Y::Y(Y&&)
source.cpp:1:8: note: no known conversion for argument 1 from 'Z' to 'Y&&'
Is gcc correct to reject the conversion from Z to Y via int, or does the standard indeed permit this usage?
I considered the context of the mentioned direct-initialization; per the definition of direct-initialization to class type in 8.5:16, a constructor is called with the initializer expression as its arguments, which therefore are converted to the parameter type by an implicit conversion sequence (13.3.3.1). Since an implicit conversion sequence is an implicit conversion (4:3), and thus models copy-initialization (8.5:14) and not direct-initialization, the language in 12.3.2:2 must be referring to the expression as a whole.
Note also that this isn't a violation of 12.3:4 (multiple user-defined conversions); the same compiler is happy with the same code with explicit removed (as are Clang and Comeau):
struct Y { Y(int); };
struct Z { operator int(); };
void h(Z z) {
Y y1(z); // direct-initialization
}
I think Jesse Good has identified the distinction between the operator Y and operator int cases in 13.3.1.4:1, but there's a third case that I'm still concerned by:
struct X {};
struct Y { Y(const X &); };
struct Z {
explicit operator X() const;
};
void h(Z z) {
Y y1(z); // direct-initialization via class-type X
}
The initialization of the temporary X to be bound to the single const X & parameter of the constructor of Y proceeds in a direct-initialization context per 13.3.1.4:1, with T as X and S as Z. I think this clause is incorrect and should read:
13.3.1.4 Copy-initialization of class by user-defined conversion [over.match.copy]
1 - [...] When initializing a temporary to be bound to the first parameter
of a constructor that takes a reference to possibly cv-qualified T as its first argument, called with a single argument in the context of direct-initialization of an object of type "cv2 T", explicit conversion functions are also considered. [...]
For the avoidance of confusion, I think 12.3.2:2 should also be amended:
12.3.2 Conversion functions [class.conv.fct]
2 - A conversion function may be explicit (7.1.2), in which case it is only considered as a user-defined conversion for direct-initialization (8.5) in certain contexts (13.3.1.4, 13.3.1.5, 13.3.1.6). [...]
Any comments on the above?
According to 8.5 and 13.3.1.3 the constructors of Y are considered and the best one is picked via overload resolution. In this case the relevant constructors are Y(int); and the copy and move constructors. In the process of overload resolution 13.3.2 Viable functions [over.match.viable] specifies this:
3 Second, for F to be a viable function, there shall exist for each argument an implicit conversion sequence (13.3.3.1) that converts that argument to the corresponding parameter of F. [...]
For all those constructors there is no such conversion from Z to either int or one of the flavours of Y. To convince ourselves, let's investigate what the Standard says about implicit conversion sequences in 13.3.3.1 Implicit conversion sequences [over.best.ics]:
1 An implicit conversion sequence is a sequence of conversions used to convert an argument in a function call to the type of the corresponding parameter of the function being called. The sequence of conversions is an implicit conversion as defined in Clause 4, which means it is governed by the rules for initialization of an object or reference by a single expression (8.5, 8.5.3).
If we cross-reference Clause 4, then we learn that an implicit conversion is defined in terms of copy-initialization (i.e. T t=e;, where T is int and e is z):
(§4.3) An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed, for some invented temporary variable t (8.5). [...]
So I take 12.3.2:2 not to apply for this initialization, which happens in the larger context of a direct initialization. Doing otherwise would contradict with this latest paragraph.
As noted in Luc Danton's answer, implicit conversion is defined in terms of copy initialization. Then, if we look at 13.3.1.4:1[Copy-initialization of class by user-defined conversion]:
When the type of the initializer expression is a class type “cv S”,
the non-explicit conversion functions of S and its base classes are
considered. When initializing a temporary to be bound to the first
parameter of a constructor that takes a reference to possibly
cv-qualified T as its first argument, called with a single argument in
the context of direct-initialization, explicit conversion functions
are also considered. Those that are not hidden within S and yield a
type whose cv-unqualified version is the same type as T or is a
derived class thereof are candidate functions. Conversion functions
that return “reference to X” return lvalues or xvalues, depending on
the type of reference, of type X and are therefore considered to yield
X for this process of selecting candidate functions.
If I understand this correctly, the first one works because the conversion function yields a Y and is therefore a candidate function as noted by the second emphasized part in the quote, however, in your second case, the set of candidate functions is empty because there is no conversion function to Y and no non-explicit conversion functions as noted by the first emphasized part.
Concerning the third case:
After finding defect report 1087, it seems clear that the intention was to allow, copy, move and template constructors when direct-initializing an object of cv2 T as you mention. If you read the first passage of 13.3.1.4, it says Assuming that “cv1 T” is
the type of the object being initialized, with T a class type, so I think that implies of an object of type "cv2 T" that you mention. However, (after reading it over), it seems that the change due to the defect report has caused the wording to become vague and not cover the third case you propose.
I am no language lawyer, however the wording of the standard implies to me that marking a conversion operator as explicit requires that you explicity specify the conversion type (i.e. int) as part of the initialisation of object y1. With the code Y y1(z), it would appear that you're relying on an implicit conversion, since the type you specify for variable y1 is Y.
Therefore, I would expect correct usage of the explicit conversion operator in this situation to be:
Y y1( int(z) );
Or, since you are effectively specifying a cast, preferably
Y y1( static_cast<int> (z) );

Overload resolution and arrays: which function should be called?

Consider the following program:
#include <cstddef>
#include <cstdio>
void f(char const*&&) { std::puts("char const*&&"); } // (1)
void f(char const* const&) { std::puts("char const* const&"); } // (2)
template <std::size_t N>
void f(char const (&)[N]) { std::puts("char const(&)[N]"); } // (3)
int main()
{
const char data[] = "a";
f(data);
}
Which f should be called? Why?
The latest released versions of three compilers disagree on the answer to this question:
(1) is called when the program is compiled using g++ 4.5.2
(2) is called when the program is compiled using Visual C++ 2010 SP1
(3) is called when the program is compiled using Clang 3.0 (trunk 127530)
Have the overload resolution rules changed substantially in different C++0x drafts? Or, are two of these compilers really just completely wrong? Which overload is the correct overload to be selected per the latest C++0x draft?
First, the conversion sequence of all three is the same, except that for the first two, there is an lvalue transformation (lvalue to rvalue conversion), which however is not used in ordering conversion sequences. All three are exact matches (the function template specialization has parameter type char const(&)[2]).
If you iterate over the rules at 13.3.3.2p3, you stop at this paragraph
S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.
A conversion sequence cannot be formed if it requires binding an rvalue reference to an lvalue, the spec says at 13.3.3.1.4p3. If you look at how reference binding works at 8.5.3p5 last bullet, it will create a temporary (I think they meant rvalue temporary) of type char const* from the array lvalue and bind the reference to that temporary. Therefor, I think (1) is better than (2). Same holds for (1) against (3), although we wouldn't need this because (3) is a template so in a tie, we would choose (1) again.
In n3225, they changed the reference binding rules so that rvalue references can bind to initializer expressions that are lvalues, as long as the reference will be bound to an rvalue (possibly created by converting the initializer properly before). This could influence the handling by Visual C++, which may not be up to date here.
I'm not sure about clang. Even if it would ignore (1), then it would end up in a tie between (2) and (3), and would need to choose (2) because it's a non-template.
I think that 8.5.3p5 last bullet is confusing because it says "Otherwise a temporary of type ..". It's not clear whether the temporary is regarded as an lvalue or as an rvalue by 13.3.3.1.4p3, which means I'm not sure how the following should really behave according to the exact words of the spec
void f(int &);
void f(int &&);
int main() {
int n = 0;
f(n);
}
If we assume the temporary is treated as an rvalue by clause 13, then we bind an rvalue ref to an rvalue in the second function and an lvalue in the first. Therefor, we will choose the second function and then get a diagnostic by 8.5.3p5 last bullet because T1 and T2 are reference-related. If we assume the temporary is treated as an lvalue by clause 13, then the following would not work
void f(int &&);
int main() {
f(0);
}
Because we would bind an rvalue ref to an lvalue which by clause 13 will make the function non-viable. And if we interpret "binding an rvalue ref to an lvalue" to refer to the initializer expression instead of the final expression bound to, we won't accept the following
void f(float &&);
int main() {
int n = 0;
f(n);
}
This however is valid as of n3225. So there seems to be some confusion - I sent a DR to the committee about this.
I claim that #3 is the function chosen by a conforming compiler.
(1) is better than (2) because "Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference."
(3) is better than both (1) and (2) because it is an identity conversion (the others are exact match conversions) and "Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence)"
Template vs non-template is only considered when neither conversion is better "or, if not that..."
oddly enough though, Comeau prefers (2) over (3). This test case fails to compile:
#include <cstddef>
#include <cstdio>
// (1) removed because Comeau doesn't support rvalue-references yet
char f(char const* const&) { std::puts("char const* const&"); return 0; } // (2)
template <std::size_t N>
int f(char const (&)[N]) { std::puts("char const(&)[N]"); return 0; } // (3)
int main()
{
const char data[] = "a";
switch (0) {
case sizeof(char):
break;
case sizeof(f(data)):
break;
}
}
This is a community wiki answer for collecting snippets from the standard (draft 3225).
section 13.3.3 "Best viable function" [over.match.best]
Define ICSi(F) as follows:
if F is a static member function, ICS1(F) is defined such that ICS1(F) is neither better nor worse than
ICS1(G) for any function G, and, symmetrically, ICS1(G) is neither better nor worse than ICS1(F); otherwise,
let ICSi(F) denote the implicit conversion sequence that converts the i-th argument in the list to the
type of the i-th parameter of viable function F. 13.3.3.1 defines the implicit conversion sequences and
13.3.3.2 defines what it means for one implicit conversion sequence to be a better conversion sequence or worse conversion sequence than another.
Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2)
or, if not that,
the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the entity being initialized) is a better conversion sequence than the standard conversion sequence from the return type of F2 to the destination type
or, if not that,
F1 is a non-template function and F2 is a function template specialization
or, if not that,
F1 and F2 are function template specializations, and the function template for F1 is more specialized
than the template for F2 according to the partial ordering rules described in 14.5.6.2.
If there is exactly one viable function that is a better function than all other viable functions, then it is the one selected by overload resolution; otherwise the call is ill-formed.
section 13.3.3.1.4 "Reference binding" [over.ics.ref]
When a parameter of reference type binds directly (8.5.3) to an argument expression, the implicit conversion
sequence is the identity conversion, unless the argument expression has a type that is a derived class of the
parameter type, in which case the implicit conversion sequence is a derived-to-base conversion (13.3.3.1). If the parameter binds directly to the result of applying a conversion function to the
argument expression, the implicit conversion sequence is a user-defined conversion sequence (13.3.3.1.2), with the second standard conversion sequence either an identity conversion or, if the conversion function
returns an entity of a type that is a derived class of the parameter type, a derived-to-base Conversion.
When a parameter of reference type is not bound directly to an argument expression, the conversion sequence
is the one required to convert the argument expression to the underlying type of the reference according
to 13.3.3.1. Conceptually, this conversion sequence corresponds to copy-initializing a temporary of the
underlying type with the argument expression. Any difference in top-level cv-qualification is subsumed by
the initialization itself and does not constitute a conversion.
section 13.3.3.2 "Ranking implicit conversion sequences" [over.ics.rank]
13.3.3.2 defines a partial ordering of implicit conversion sequences based on the relationships better conversion
sequence and better conversion. If an implicit conversion sequence S1 is defined by these rules to be a better
conversion sequence than S2, then it is also the case that S2 is a worse conversion sequence than S1. If
conversion sequence S1 is neither better than nor worse than conversion sequence S2, S1 and S2 are said to
be indistinguishable conversion sequences.
When comparing the basic forms of implicit conversion sequences (as defined in 13.3.3.1)
a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and
a user-defined conversion sequence (13.3.3.1.2) is a better conversion sequence than an ellipsis conversion sequence (13.3.3.1.3).
Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of
the following rules applies:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence)
or, if not that,
the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below
or, if not that,
S1 and S2 differ only in their qualification conversion and yield similar types T1 and T2 (4.4), respectively, and the cv-qualification signature of type T1 is a proper subset of the cv-qualification signature of type T2.
or, if not that,
S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a
non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to
an rvalue and S2 binds an lvalue reference.

Why const for implicit conversion?

After extensive reading of ISO/IEC 14882, Programming language – C++ I'm still unsure why const is needed for implicit conversion to a user-defined type with a single argument constructor like the following
#include <iostream>
class X {
public:
X( int value ) {
printf("constructor initialized with %i",value);
}
}
void implicit_conversion_func( const X& value ) {
//produces "constructor initialized with 99"
}
int main (int argc, char * const argv[]) {
implicit_conversion_func(99);
}
Starting with section 4 line 3
An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed, for some invented temporary variable t (8.5). Certain language constructs require that an expression be converted to a Boolean value. An expression e appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t (8.5). The effect of either implicit conversion is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is an lvalue reference type (8.3.2), and an rvalue otherwise. The expression e is used as an lvalue if and only if the initialization uses it as an lvalue.
Following that I found the section on initializers related to user-defined types in 8.5 line 6
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.
Finally I ended up at 12.3 line 2 about user-defined conversions which states
User-defined conversions are applied only where they are unambiguous (10.2, 12.3.2).
Needless to say, 10.2 and 12.3.2 didn't answer my question.
Can someone shed some light on what effect const has on implicit conversions?
Does the use of const make the conversion "unambiguous" per 12.3 line 2?
Does const somehow affect lvalue vs. rvalue talked about in section 4?
It doesn't really have much to do with the conversion being implicit. Moreover, it doesn't really have much to do with conversions. It is really about rvalues vs. lvalues.
When you convert 99 to type X, the result is an rvalue. In C++ results of conversions are always rvalues (unless you convert to reference type). It is illegal in C++ to attach non-const references to rvalues.
For example, this code will not compile
X& r = X(99); // ERROR
because it attempts to attach a non-const reference to an rvalue. On the other hand, this code is fine
const X& cr = X(99); // OK
because it is perfectly OK to attach a const reference to an rvalue.
The same thing happens in your code as well. The fact that it involves an implicit conversion is kinda beside the point. You can replace implicit conversion with an explicit
one
implicit_conversion_func(X(99));
and end up with the same situation: with const it compiles, without const it doesn't.
Again, the only role the conversion (explicit or implicit) plays here is that it helps us to produce an rvalue. In general, you can produce an rvalue in some other way and run into the same issue
int &ir = 3 + 2; // ERROR
const int &cir = 3 + 2; // OK
Per section 5.2.2 paragraph 5, when an argument to a function is of const reference type, a temporary variable is automatically introduced if needed. In your example, the rvalue result of X(99) has to be put into a temporary variable so that that variable can be passed by const reference to implicit_conversion_func.