In constructor, candidate expects 1 argument, 0 provided

39,074

Solution 1

You need a default constructor or initialize cat objects in twoCats constructor initilization list explicitly to avoid default construction.

Why does the constructor for twoCats try to call the default constructor for cat? Surely it does not need to construct an instance of cat as when twoCats is initialised it will be passed an already initialised instance of cat which will have been passed the int height argument?

It needs to construct default values for cat objects

private:
    cat firstCat;
    cat secondCat;

in class twoCats because you didn't initialize them. In your constructor

cat::cat (int inputHeight)
{
    height = inputHeight;
    ^^^^^^^^^^^^^^^^^^^^
}   // this is assignment

this is assignment to already created objects.

The rule is as follows: if you don't initialize instances explicitly in ctor initialization list then

  1. Default ctor is called
  2. You eventually assign to already default constructed objects in ctor body.

Thus you face penalty of having additional calls if you don't initialize in initialization list.

C++ Standard n3337 § 12.6.2/10 Initializing bases and members

In a non-delegating constructor, initialization proceeds in the following order:

— First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

— Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).

Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

Finally, the compound-statement of the constructor body is executed.

[ Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. — end note ]

Here is a code demo.

Solution 2

I would initialize the class twoCats like this:

class twoCats
{
private:
    cat firstCat;
    cat secondCat;
public:
    twoCats (const cat& theFirstCat, const cat& theSecondCat)
        : firstCat (theFirstCat), secondCat (theSecondCat)
    {
    }
};

The important part here is the colon after the constructor :. It starts the member initialization list, which is the place where, if possible, all your class data members should be initialized.

Initialization of data members is quite a complex issue in C++, I suggest you google it.

In particular, since you have two members of class type, the compiler, no matter what, tries to initialize them in your constructor. It does so for every cat, which probably is the reason you get the error message block twice. In its default, the compiler tries to initialize your cat data members using a default constructor, i.e. one without arguments. Unfortunately, cat does not have a default constructor, since you declared one with one argument. In other words, each cat has to be initialized with one argument (or copied, or moved in C++11).

I do not recommend declaring an additional constructor to cat without arguments: It seems that there is no "default hight" of a cat, and the -1 suggested by another answer is very strange: It doesn't seem to construct a valid object, and you'd have to check for this default value before using any of cat's member functions.

EDIT: This is from a format point of view. As for the semantics of your program, it might be wrong to copy the cats. Maybe you do need a reference (or a pointer) to the objects you initialized your twoCats with, maybe not.

Solution 3

Both cat instances have to be initialized, at the point when they start existing.

To avoid this you can defer each instance creation to when you need it.

A simple and safe way to do that is to use a std::vector to hold the instances.

class cat
{
    private:
        int height;
    public:
        cat (int inputHeight);
};

cat::cat (int inputHeight)
{
    height = inputHeight;
}

#include <vector>
#include <utility>

class twoCats
{
    private:
        std::vector<cat> cats_;

    public:
        twoCats (cat theFirstCat)
        { cats_.push_back( std::move( theFirstCat ) ); }

        void addSecondCat (cat theSecondCat)
        { cats_.push_back( std::move( theSecondCat ) ); }
};

int main() {return 0;}

Alternatively, you might use boost::optional.

Or allocate the instances dynamically (use a smart pointer such as unique_ptr to manage lifetime then).

Or, let cats be default-constructible.


As noted by "thang" in a comment, the original design does not guarantee that a twoCats has two cats. It can have just one cat, or three or more cats. So it would be a good idea to change the design.

For example, have a constructor that takes two cat arguments, or cat heights.

Or for another example, changing the name of twoCats.

Share:
39,074
texasflood
Author by

texasflood

Updated on July 22, 2022

Comments

  • texasflood
    texasflood almost 2 years

    This is the code:

    class cat
    {
        private:
            int height;
        public:
            cat (int inputHeight);
    };
    
    cat::cat (int inputHeight)
    {
        height = inputHeight;
    }
    
    class twoCats
    {
        private:
            cat firstCat;
            cat secondCat;
        public:
            twoCats (cat theFirstCat);
            void addSecondCat (cat theSecondCat);
    };
    
    twoCats::twoCats (cat theFirstCat)
    {
        firstCat = theFirstCat;
    }
    
    void twoCats::addSecondCat (cat theSecondCat)
    {
        secondCat = theSecondCat;
    }
    
    int main() {return 0;}
    

    And these are the errors:

    main.cpp: In constructor ‘twoCats::twoCats(cat)’:
    main.cpp:24:34: error: no matching function for call to ‘cat::cat()’
    main.cpp:24:34: note: candidates are:
    main.cpp:9:1: note: cat::cat(int)
    main.cpp:9:1: note:   candidate expects 1 argument, 0 provided
    main.cpp:1:7: note: cat::cat(const cat&)
    main.cpp:1:7: note:   candidate expects 1 argument, 0 provided
    main.cpp:24:34: error: no matching function for call to ‘cat::cat()’
    main.cpp:24:34: note: candidates are:
    main.cpp:9:1: note: cat::cat(int)
    main.cpp:9:1: note:   candidate expects 1 argument, 0 provided
    main.cpp:1:7: note: cat::cat(const cat&)
    main.cpp:1:7: note:   candidate expects 1 argument, 0 provided
    

    I don't understand the following:

    1. Why does the constructor for twoCats try to call the default constructor for cat? Surely it does not need to construct an instance of cat as when twoCats is initialised it will be passed an already initialised instance of cat which will have been passed the int height argument?
    2. Why is the same block of error messages shown twice? I called g++ main.cpp on Ubuntu 12.04.
  • thang
    thang over 9 years
    This is all fine, but I think that it doesn't answer the questions, and can introduce performance problems
  • 4pie0
    4pie0 over 9 years
    @Cheersandhth.-Alf True, thank you, chnaged
  • thang
    thang over 9 years
    I think this is good except that you can accidentally add second cat many times. In which case, twoCats is not two cats, which is a recipe for bugs later on.
  • Cheers and hth. - Alf
    Cheers and hth. - Alf over 9 years
    @thang: true. i kept the design. if one starts changing the design it just becomes ... something different.
  • thang
    thang over 9 years
    well I think if there is a constraint that it must be twoCats (2 cats), then the implementation either should not use vector (for example, 2 explicit smart pointers) or use vector and check for size. On that note, addSecondCat should probably be setSecondCat. The class name and (some of the) declaration calls for 1-2 association.
  • thang
    thang over 9 years
    well, the design isn't wrong. it depends on the requirements :p It could be that the intention is to have a set of at most 2 cats and at least 1 cat.
  • Cheers and hth. - Alf
    Cheers and hth. - Alf over 9 years
    @thang: yeah, or change the name. but since there's no way to access the cats or even count them, any incorrect usage will probably go unnoticed. ;-)
  • thang
    thang over 9 years
    Yes, it's "write only memory"...
  • texasflood
    texasflood over 9 years
    Thanks very much for the clarification, perfect answer, this is exactly what I wanted!
  • texasflood
    texasflood over 9 years
    Thanks, this was very helpful, I think for my actual problem (this cats program was just a MWE) I will use pointers to solve the issue
  • texasflood
    texasflood over 9 years
    The MWE I presented wasn't very well thought out, yes, for this example I really should have ensured the constructor could only ever accept two cats so there should always be two cat objects :)
  • texasflood
    texasflood over 9 years
    Sorry guys, but this was just a MWE, there are obviously many design flaws in it, I should have probably said so in the question :P