
An ellipsis conversion sequence occurs when an argument in a function call is matched with the ellipsis parameter specification of the function called. ⸺ [over.ics.ellipsis]/1
If any of those has Conversion rank, the sequence has Conversion rank; otherwise, if any of those has Promotion rank, the sequence has Promotion rank; otherwise, the sequence has Exact Match rank. ⸺ [over.ics.scs]/3 When a parameter of reference type binds directly (9.4.4) 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. ⸺ [over.ics.ref]/1
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if, when you follow the rules in §13.3.3.2 from top to bottom—at each step asking “is S1 better than S2?”—you find a rule that favors S1 and stop there; if you exhaust all the rules without a decision, S1 and S2 remain indistinguishable:
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, (PH: this step serves as a short circuit)
#include <iostream>
void f(int) { std::cout << "A" << std::endl; }
void f(short) { std::cout << "B" << std::endl; }
int main() {
int a = 1;
f(a); // Calls f(int)
return 0;
}
#include <iostream>
void f(int*) { std::cout << "A" << std::endl; }
void f(const int*) { std::cout << "B" << std::endl; }
int main() {
int a = 1;
f(&a); // Calls f(int*)
return 0;
}
S1 is better than the rank of S2, or, if not that,
#include <iostream>
void f(int) { std::cout << "A" << std::endl; }
void f(unsigned short) { std::cout << "B" << std::endl; }
int main() {
short a = 1;
f(a);
return 0;
}
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.
#include <iostream>
void foo(int*){ std::cout << "A\n"; };
void foo(const int*){ std::cout << "B\n"; };
int main() {
int value = 1;
foo(&value);
return 0;
}
#include <iostream>
void foo(int * * volatile * volatile const * const){ std::cout << "A\n"; };
void foo(int * * const volatile * volatile const * const){ std::cout << "B\n"; };
int main() {
int value = 1;
int * r = &value;
int * * volatile s = &r;
int * * volatile * t = &s;
int * * volatile * const * const u = &t;
foo(u); // A, both foo() are overloaded
return 0;
}
#include <iostream>
void foo(int * * volatile * volatile const * const){ std::cout << "A\n"; };
void foo(int * volatile * volatile * volatile const * const){ std::cout << "B\n"; };
int main() {
int value = 1;
int * r = &value;
int * * volatile s = &r;
int * * volatile * t = &s;
int * * volatile * const * const u = &t;
foo(u); // A, not because of overloading but u cannot be converted to
// int * volatile * volatile * volatile const * const
return 0;
}
#include <iostream>
void foo(int * * volatile * volatile const * const){ std::cout << "A\n"; };
void foo(int * * const volatile * const * const){ std::cout << "B\n"; };
int main() {
int value = 1;
int * r = &value;
int * * volatile s = &r;
int * * volatile * t = &s;
int * * volatile * const * const u = &t;
foo(u); // ambiguous
return 0;
}
S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.
#include <iostream>
void f(const int&) { std::cout << "A" << std::endl; }
void f(volatile int&) { std::cout << "B" << std::endl; }
int main() {
int a = 1;
f(a); // Error: ambiguous call
return 0;
}
#include <iostream>
void f(const volatile int&) { std::cout << "A" << std::endl; }
void f(volatile int&) { std::cout << "B" << std::endl; }
int main() {
int a = 1;
f(a); // Calls f(volatile int&)
return 0;
}

#include <string>
std::string color( "purple" );
void print( std::string );
int main() {
print( color ); // exact match: lvalue-to-rvalue conversion
return 0;
}
The argument in the call to print(color) is passed by value, an lvalue-to-rvalue conversion takes place to extract a value from color and pass it to print(string).
int ai[3];
void putValues(int *);
int main() {
// ...
putValues(ai); // exact match: array-to-pointer conversion
return 0;
}
int lexicoCompare( const string &, const string & );
typedef int (*PFI)( const string &, const string & );
void sort( string *, string *, PFI );
string as[10];
int main()
{
// ...
sort( as,
as + sizeof(as)/sizeof(as[0] - 1),
lexicoCompare // exact match:
// function-to-pointer conversion
);
return 0;
}

#include<iostream>
void print(int ){};
void print(int&){};
int main(){
int i = 1;
// print(int) requires lvalue-to-rvalue conversion (exact match)
// print(int&) is in the identity catagory (exact match)
// Following the ranking comparison,
// S1 and S2 remain indistinguishable under all subsequent tiebreaker rules.
int& ri = i;
print(ri); // Error: ambiguous
// calls print(int) since print(int) is the only one viable function
print(1);
return 0;
}
#include<iostream>
class A { };
class B : public A { };
void f(A&) {}
void f(B&) {}
int main() {
B b;
// calls f(B&), an exact match, rather than f(A&), a conversion
f(b);
}
#include<iostream>
void print(int ){};
void print(const int&){};
int main(){
short i = 1;
// print(int) requires lvalue-to-rvalue conversion
// and integral promotion (Promotion)
// print(const int&) requires promotion too
// specifically, compiler first convert the argument `short` to `int`,
// then copy-initializes a temporary `int` with the result of conversion,
// and finally binds the reference to the temporary
print(i); // Error: ambiguous
return 0;
}
#include<iostream>
class B {
public:
B(unsigned int ui):value(ui){}
private:
unsigned int value;
};
class A {
public:
A(int i):value(i){}
// Conversion A => B
operator B() {
return B(static_cast<unsigned int>(value));
}
private:
int value;
};
void foo(const B&){ std::cout << "A\n"; }
int main(){
A a(1);
// foo(const B&): user-defined conversion
// const is required as a temporary object with type B will be created.
foo(a);
}
#include<iostream>
void print(int& a){ std::cout<< a << std::endl; };
int main(){
double d = 3;
print(d);
return 0;
}
However, if the reference parameter is const, the conversion is allowed.
#include<iostream>
// Step 1: Convert double to a Temporary int (lvalue-to-rvalue conversion)
// Step 2: Bind const int& to the Temporary int
// Step 3: Use a in print()
void print(const int& a){ std::cout<< a << std::endl; };
int main(){
double d = 3;
print(d);
return 0;
}