views:

57

answers:

3

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?

+1  A: 

You are looking at the second example as if (22, 23) where a tuple (that is, a single variable that is a pair) when they are not. The function f only takes one variable. When you try to pass it two variables that will fail.

The reason the first one works is that the compiler can cast from an int to a A. However you can't cast two variables to a single variable, C++ doesn't allow that.

Niki Yoshiuchi
+7  A: 

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;
}
Tyler McHenry
@Tyler McHenry: Thanks! According to me, even `f(23)` (in my first code snip) should have failed. Even that is ugly.
Lazer
You can make `f(23)` fail by using the `explicit` keyword on the `A` constructor, i.e. `explicit A(int) : var(a) {}`. The `explicit` keyword means that the constructor must always be used explicitly, and may not be used for implicit conversions. Obviously this only make sense for constructors that take exactly one argument.
Tyler McHenry
+1  A: 

Only constructors that can be called with one argument define implicit conversions, so there are no implicit conversions possible in your second example since no constructor of A takes one argument.

Philipp