Call of overloaded constructor is ambiguous

When I try to instantiate objects of class Foo in main() of foo.cpp, I keep getting errors about how the call to the overloaded constructor is ambiguous. The compiler seems to not be able to tell between a non-reference parameter input like int num and a lreference parameter input like int &num. It also seems to not be able to tell between a non-reference parameter input and a rreference parameter input like int &&num. Why would this be? If the passed element is not any kind of reference, doesn't it make a copy of the original element and then pass the copy? Passing by lreference would mean that a pointer to the original element is passed and that the user could manipulate the original element by manipulating the reference, right? Same goes with rreferences? Why is the compiler confused when I clearly sent it something that has to be moved from one place in memory to another without creating a copy?

Relevant error readout and code that I referenced are listed below:

compiler readout
1
2
3
4
5
6
7
8
9
10
11
In function 'int main()':
27:22: error: call of overloaded 'Foo(int&, int&)' is ambiguous
27:22: note: candidates are:
11:9: note: Foo::Foo(int&&, int&&) <near match>
11:9: note:   no known conversion for argument 2 from 'int' to 'int&&'
8:9: note: Foo::Foo(int&, int&)
5:9: note: Foo::Foo(int, int)
28:18: error: call of overloaded 'Foo(int, int)' is ambiguous
28:18: note: candidates are:
11:9: note: Foo::Foo(int&&, int&&)
5:9: note: Foo::Foo(int, int)


foo.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <utility>

class Foo {
    public:
        Foo( int val1, int val2 ):
            num1{ val1 }, num2{ val2 } { }
        
        Foo( int &val1, int &val2 ):
            num1{ val1 }, num2{ val2 } { }
        
        Foo( int &&val1, int &&val2 ):
            num1{ std::move(val1) }, num2{ std::move(val2) } { }
            
            
    private:
        int num1;
        int num2;

};


int main()
{
    int num = 5;
    int num2 = 6;
    
    Foo foo(num, num2);
    Foo foo2(5, 6);
    
    return 0;
}
lvalue and rvalue reference parameters provide a means to overload functions on the value categories of their arguments.

Consider these two overloads of f:
1
2
void f(int&) {}
void f(int&&) {}

An expression like f(e) calls the first overload when e is an lvalue expression; it calls the second overload when e is an rvalue expression.

This third overload of f does not care about the value category of e.
void f(int) {}
Mixing the three functions leads to ambiguity. Which function should be called when the argument is a rvalue?

Last edited on
So because the third overload does not care about the value category, is there a way to represent a non-reference value without causing ambiguity?
How could these not be ambiguous?

1
2
3
4
5
6
7
void f(int n)        { }
void f(const int& n) { }

int main() {
    int n = 42;
    f(n); // ???
}

@dutch- I understand why it would cause ambiguity. See my question above your comment.
Then your question is senseless.
is there a way to represent a non-reference value without causing ambiguity?
References are not objects, and the type of an expression is never a reference. Given the declaration
int& x = y;
The type of the expression x is int.
What behavior do you want to see, and why do you think you want to see it?
In one of the constructors, I want a copied value to be passed in as a parameter that isn't referencing the original value. int &x, for example, would pass a pointer to the callee that can then have its original value modified or assigned to some other value. int &&x would be an rvalue that doesn't have its own assigned place in memory. Neither of these methods of passing some sort of value, from what I understand, are able to pass a value that is a copy of the original value but that can't affect the original value if the parametrized value is assigned something new.
My above comment was not meant to imply that I never was asking about why the compiler found ambiguities. I was asking about that and @mbozzi clarified it for me. Then I had a question about how I would represent a parameratized value that would be a copy of another value that cannot be altered by the callee. A reference to a value WOULD be able to be alterted by the callee as the parameratized input is pointing to an already existing place in memory. Changing the data of that parameratized input would change what data was held at that same memory address.
Last edited on
If a copy is needed, just pass-by-value? void f(int) {}
I think, the ambiguity is because you have overloaded constructors, if you just remove other two constructors (&),(&&), then you can achieve what you are saying.

Given we have may varieties of constructors, I think we have to stick to what's best for us.
Neither of these methods of passing some sort of value, from what I understand, are able to pass a value that is a copy of the original value but that can't affect the original value if the parametrized value is assigned something new.

Pardon?
If you don’t want the original objects to be changed, just declare the references const:
Foo(const int& val1, const int& val2):

Anyway that’s not a problem in your code, since you anyway copy them into the class prorperties:
num1{ val1 }, num2{ val2 }

The compiler can tell pointers and not pointers apart, if you really, really, really need that (I’m not suggesting it’s a good idea):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <utility>


class MyClass {
public:
    MyClass( int num1_arg, int num2_arg );
    MyClass( const int * const num1_arg, const int * const num2_arg );

private:
    int num1;
    int num2;
};


MyClass::MyClass( int num1_arg, int num2_arg )
    : num1 { num1_arg }
    , num2 { num2_arg }
{
    std::cout << "Arguments passed by copy\n";
}


MyClass::MyClass( const int * const num1_arg, const int * const num2_arg )
    : num1 { *num1_arg }
    , num2 { *num2_arg }
{
    std::cout << "Arguments passed by pointers\n";
}


int main()
{
    int num1 = 5;
    int num2 = 6;

    MyClass foo1(num1, num2);
    MyClass foo2(13, 666);
    MyClass foo3( &num1, &num2 );
}


Output:
Arguments passed by copy
Arguments passed by copy
Arguments passed by pointers

@Enoizat- I didn't think about "const". That qualifier would definitely accomplish what I want it to since it would eliminate ambiguity and serve to provide a non-alterable copy.
Topic archived. No new replies allowed.