Search code examples
c++constructorlanguage-featureslanguage-design

Why is only one implicit conversion allowed to resolve the parameters to a function in C++?


This works:

#include<cstdio>

class A{
public:
    A(int a):var(a){}
    int var;
};

int f(A obj) {
    return obj.var;
}

int main() {
    std::cout<<f(23);  // output: 23
    return 0;
}

while this doesn't:

#include<cstdio>

class A{
public:
    A(int a, int b):var1(a), var2(b){}
    int var1, var2;
};

int f(A obj) {
    return (obj.var1 + obj.var2);
}

int main() {

    cout<<f(22, 23); // error: conversion from 'int' to 
                     // non-scalar type 'A' requested
    return 0;
}

The second piece of code does not work, but I am not able to find a good-enough reason why it doesn't. To me, the code just looks too odd to work.

But what is the actual reason that there is only one implicit conversion allowed in situations like this? Is it a this-is-like-this-only language feature?


Solution

  • The direct answer is that the C++ grammar does not combine function arguments into aggregates under any circumstances. In no case will a call to f(a, b) be treated as a call to f(c) where c is a value constructed from some combination of a and b. That's just not how the language works. Each argument is always treated separately for overloading, implicit conversion, and all other purposes.

    As for a rationale, if what you're asking about were allowed, how would you expect this situation to be resolved:

    class A {
      public:
        A(int a) : v1(a) {}
      private:
        int v1;
    };
    
    class B {
      public:
        B(int a, int b) : v1(a), v2(b) {}
      private:
        int v1;
        int v2;
    };
    
    class C {
      public:
        C(int a, int b, int c) : v1(a), v2(b), v3(c) {}
      private:
        int v1;
        int v2;
        int v3;
    };
    
    void f(A obj1, A obj2) { /* ... */ }
    
    void f(B obj) { /* ... */ }
    
    void g(A obj1, B obj2) { /* ... */ }
    
    void g(B obj1, int i) { /* ... */ }
    
    void g(C obj) { /* ... */ }
    
    int main() {
       f(22, 23); // Call f(A,A) or f(B)? 
       g(22, 23, 24); // Call g(A,B) or g(B, int), or g(C)? 
       return 0;
    }