0

A class is implemented as follows, there are three construction methods, and then an empty sequence is declared through a macro to initialize the class,

#include <iostream>
#include <string>
#include <vector>

using namespace std;

#define EMPTY_LIST {}

class sample{
private:
    typedef uint64_t s_type;
    vector<s_type> _vec;

public:
  sample() {}
  sample(const sample& other) : _vec(other._vec) {{
      cout << "const lvalue" << endl;      
  }}
  sample(sample&& other) : _vec(std::move(other._vec)) {{
      cout << "rvalue ref" << endl;      
  }}
  sample(const vector<size_t>& array) {
      cout << "array" << endl;      
  };
};


int main(){
    sample ft(EMPTY_LIST);
    return 0;
}

But the compilation reports an error. What is the reason?

# g++ sample.cc -o sample
sample.cc: In function ‘int main()’:
sample.cc:30:25: error: call of overloaded ‘sample(<brace-enclosed initializer list>)’ is ambiguous
     sample ft(EMPTY_LIST);
                         ^
sample.cc:22:3: note: candidate: ‘sample::sample(const std::vector<long unsigned int>&)’
   sample(const vector<size_t>& array) {
   ^~~~~~
sample.cc:19:3: note: candidate: ‘sample::sample(sample&&)’
   sample(sample&& other) : _vec(std::move(other._vec)) {{
   ^~~~~~
sample.cc:16:3: note: candidate: ‘sample::sample(const sample&)’
   sample(const sample& other) : _vec(other._vec) {{
   ^~~~~~

maybe the empty array defined by the macro is an empty instance? So is it ambiguous? Or is it because of the C++ standard?

6
  • Maybe try marking the constructors as explicit? Commented Sep 4, 2020 at 2:57
  • @Joe after using explicit, it still fails to compile. I am not sure what data type is defined by using a macro to define a {}. Commented Sep 4, 2020 at 3:02
  • @d4rk4ng31 my expectation should be to call sample(const vector<size_t>& array) Commented Sep 4, 2020 at 3:04
  • 4
    The compiler is telling you that the empty list {} can be equally converted to any of the 3 types you've provided as constructor parameters. You either need to give it a reason to prefer one over the others, or change it from {} to something else that is more explicit. Commented Sep 4, 2020 at 3:10
  • 1
    @MarkRansom, why don't you consider posting it as an answer? Its to the point and short :) Commented Sep 4, 2020 at 3:22

2 Answers 2

2

Seeing from this comment of yours, what you probably want is:

std::vector<int> vec;
sample ft(vec);


You see, what you are essentially calling is: sample ft({}); which is ambiguous even with explicit keyword. You need to understand what brace-initialisation (thanks @David C. Rankin for the link :) ) is!

Another way would be:

sample(const vector<size_t>&& array) {
    cout << "array" << endl;      
};

/*call: */
sample ft(std::vector<int>{});
Sign up to request clarification or add additional context in comments.

Comments

1

When the preprocessor substitutes EMPTY_LIST with {}, you end up with:

sample ft({});

The first line of your error explains what is happening here:

sample.cc:30:25: error: call of overloaded ‘sample(<brace-enclosed initializer list>)’ is ambiguous

In other words, the compiler doesn't know which constructor you want to call. There are multiple functions that the compiler can choose from that fit your syntax, but C++ requires that your syntax be explicit enough that the compiler does not have to guess which function you intend to call. The error lists the following functions as possible candidates to choose from based on your syntax:

// 1.) Constructor using vector
sample(const vector<size_t>& array)

// 2.) Move constructor
sample(sample&& other)

// 3.) Copy constructor
sample(const sample& other)

The compiler doesn't assume that you want to create an empty std::vector when you use {} because {} doesn't actually imply vector/array/etc. In fact, the {} can be used to initialize any type of object.

For example, you can initialize an int to value 5 using the syntax: int x{5};. Similarly, you can initialize multiple fields in an object at once using braces:

struct A {
    int i;
    float f;
    std::string s;
};

// Prints out the message "i: 5 f: 7 s: hi"
A a { 5, 7.0f, "hi" };
std::cout << "i: " << a.i << " f: " << a.f << " s: " << a.s << std::endl;

From this example, hopefully you can see that, in your example, the {} can be used to initialize either an std::vector or a new sample object, so the compiler must consider the copy constructor and move constructor, not just the constructor that takes in a vector!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.