On 22.08.2015 20:43, Stefan Ram wrote:
> [snip]
>
> #include <iostream>
>
> struct alpha { alpha(){ ::std::cout << "alpha\n"; }};
> struct beta { beta( alpha ){ ::std::cout << "beta( alpha )\n"; }};
> struct gamma { gamma( beta ){ ::std::cout << "gamma( beta )\n"; }};
>
> int main()
> {
> { beta b = alpha{}; /*
> gamma g = alpha{}; */ }
There is no direct way to construct a right hand side gamma from an
alpha, because that would involve a user defined conversion. The
compiler doesn't check such, it merely checks whether the actual
argument matches the formal argument type of some gamma constructor. It
/would/ match if alpha were derived from beta, but that's not a
conversion, that's an IS-A relationship.
> { beta b{ alpha{} };
> gamma g{ alpha{} }; }}
Here possible conversions to the formal argument type, are considered,
just as with any function call.
> alpha
> beta( alpha )
>
> alpha
> beta( alpha )
>
> alpha
> beta( alpha )
> gamma( beta )
>
> That is, the compiler used did /not/ use the sequence
> of two conversions in the case of /copy/ initialization.
> (This has been commented out above, because it gives an
> error message.)
>
> But it /did/ use the sequence in the case of /direct/
> initialization.
Well, there's only a single IMPLICIT conversion, namely from actual
argument type alpha to formal argument type beta. But the expression
gamma( alpha() )
is a conversion from alpha to gamma by way of beta. It involves two user
defined conversions, but crucially, only a single implicit one.
Here is a similar program that I think better illustrates how the limit
on number of implicit user defined conversions can be reached, and then
affects whether the code compiles or not:
#include <iostream>
using namespace std;
struct alpha { alpha() { cout << "alpha()" << endl; } };
struct beta { beta() { cout << "beta()" << endl; } };
struct gamma
{
operator alpha() { cout << "gamma -> alpha" << endl; return
alpha(); }
gamma( beta ){ cout << "gamma( beta )" << endl; }
gamma( alpha ) { cout << "gamma( alpha )" << endl; }
gamma( gamma& ) { cout << "gamma( gamma& )" << endl; }
};
int main()
{
//gamma g = beta(); // Over the limit.
gamma g = gamma( beta() ); // OK, invoking `operator alpha()`
}
Output:
beta()
gamma( beta )
gamma -> alpha
alpha()
gamma( alpha )
This was the trick employed by old `std::auto_ptr`. Or to be precise,
the trick that got standardized. It evolved a bit, as I recall.