0

I've recently begun using C++ to try and program an Arduino library for a project I'm working on. In this library, I'm including another library published on GitHub, which I assume compiles on its own. As a part of the constructor for my class, I am creating and assigning an object of the published class. When I call the constructor, which takes two integers, with two integers, I get the following error:

Cell.cpp:7:31: error: no match for call to '(DS3904) (int&, int)'
 _digipot(digipotAddress, 2);
                           ^

EDIT: I was told by the comments before I edited this that a reference should work out just fine in this instance. Here is the library I am using. Following are the header file:

Cell.h:

#ifndef Cell_h
#define Cell_h

#include "Arduino.h"
#include "DS3904.h"

class Cell {
    public:
        Cell(int cellNumber, int digipotAddress, int resistorAddress);
        void setCellVoltage(int voltage);
        int getCellNumber();
    private:
        unsigned int _cellNo;
        unsigned int _resistorAddress;
        DS3904 _digipot;
};

#endif

And the actual C++ file, Cell.cpp (note that some unused functions are omitted from this one):

#include "Arduino.h"
#include "Cell.h"

Cell::Cell(int cellNumber, int digipotAddress, int resistorAddress) {
    _digipot(digipotAddress, 2);
    _cellNo = cellNumber;
    _resistorAddress = resistorAddress;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Vijay Shastri
  • 53
  • 1
  • 5
  • 5
    `int&` is not a pointer, and is not "dereferenced". You'll want to learn about _references_ in C++ to understand better. – Drew Dormann Oct 05 '21 at 00:19
  • Can you show the definition of the constructor you're trying to call and enough of the actual calling code to see what `digitpotAddress` is? – Nathan Pierson Oct 05 '21 at 00:21
  • *I've recently begun using C++ to try and program an Arduino library for a project I'm working on.* -- To be honest with you, you can't just jump into non-trivial projects using C++ unless you know C++. C++ isn't PHP or some other scripting language that you can learn in a few hours and have a cheat sheet on hand. It is one of the most complex computer languages out there, and can only be learned going through the process of starting from scratch, reading peer-reviewed C++ books, etc. – PaulMcKenzie Oct 05 '21 at 00:21
  • This needs a [mcve]. We need to see the declarations of everything in order to check their types. – Nate Eldredge Oct 05 '21 at 00:28
  • I'm torn. Solving the error would require a [mre], but the question presented here isn't asking how to solve the error. It's asking why a "pointer" can't be dereferenced, and the answer is _"it's not a pointer"_. They are presumably confusing `int &` and `int *`. – Drew Dormann Oct 05 '21 at 00:30
  • 1
    @VijayShastri Assuming you are using [this library](https://github.com/emdzej/DS3904), I see the constructor in question defined as `DS3904(int deviceAddress, int model);` so even if `digipotAddress` were declared as an `int&` rather than an `int`, it should pass just fine. So please provide a [mcve] of your `Cell` class so we can have a chance to see what is really happening. – Remy Lebeau Oct 05 '21 at 00:32
  • 1
    To construct an object that's a member of your class, the call to the constructor needs to be part of a [member initializer list](https://www.learncpp.com/cpp-tutorial/constructor-member-initializer-lists/), not code in the body of the constructor. Indeed your constructor should probably consist of just a member initializer list and an empty body. – Nate Eldredge Oct 05 '21 at 01:00
  • By the time the body of the constructor starts to run, all the members need to have been constructed already, so it's too late to do it again. With `_digipot(digipotAddress, 2);` you may think you're trying to call a constructor, but it's parsed (I think) as an attempt to call an overloaded `DS3904::operator()`, which doesn't exist. The error message could be clearer about that. – Nate Eldredge Oct 05 '21 at 01:05
  • 2
    `_digipot(digipotAddress, 2);` -- That is not how you construct a member that requires arguments. [See what the member initialization list is all about](https://en.cppreference.com/w/cpp/language/constructor). That should be `Cell::Cell(int cellNumber, int digipotAddress, int resistorAddress) : _digipot(digipotAddress, 2) { code }`. This is exactly the point I made in the earlier comment -- the member initialization list is something you should have known of, but trying to learn C++ by guessing or trial-and-error is not the way to properly learn the language. – PaulMcKenzie Oct 05 '21 at 01:14
  • 1
    Btw, when I compile this, I get an earlier error message: `no matching function for call to DS3904::DS3904()`. That could be more of a hint: it shows that you are trying to call a default constructor (which is what you get if you don't include `_digipot` in your member initialization list), and there isn't one. Usually it's more useful to focus on the *first* compiler error message instead of the last one - and in any case, when you ask for help with compilation errors, it helps to show *all* the messages. – Nate Eldredge Oct 05 '21 at 01:17

1 Answers1

1

This actually doesn't have anything to do with pointers or references. You've got the wrong syntax for calling the constructor of the member object _digipot, and your code gets parsed as something different that results in a confusing error message.

You have a class Cell which has a member _digipot that is of type class DS3904. So your constructor for Cell needs to construct _digipot, but in C++, this isn't done by calling _digipot's constructor in the body of your Cell constructor. Instead it needs to be done in a member initializer list. So the correct way to write your constructor Cell::Cell would be:

Cell::Cell(int cellNumber, int digipotAddress, int resistorAddress)
    : _digipot(digipotAddress, 2),
      _cellNo(cellNumber),
      _resistorAddress(resistorAddress) 
{
    // no code needed in the body
}

The idea is that in C++, there should never be any point in the program where you can see an object that exists but has not been fully constructed, with the one exception being that object's own constructor. If it was up to the body code of Cell::Cell to call _digipot's constructor, then it could also try to access _digipot before calling the constructor, at which point it would find it in an unconstructed state. So _digipot has to be constructed before the body of Cell::Cell starts execution, yet there still needs to be a way to specify which constructor of _digipot should be called, and with what arguments. Member initializer lists were invented to achieve this.

(You don't technically have to include the int members _cellNo and _resistorAddress in the member initializer list: if you leave them out, they get initialized to indeterminate values, and you can then assign to them in the body of the constructor like your existing code does. But it's cleaner to do everything in the member initializer list.)


So where did the weird error message come from? There are actually two error messages for your code, of which you only posted the second one, and the first one is the more informative one:

Cell-old.cpp: In constructor ‘Cell::Cell(int, int, int)’:
Cell-old.cpp:4:67: error: no matching function for call to ‘DS3904::DS3904()’
    4 | Cell::Cell(int cellNumber, int digipotAddress, int resistorAddress) {
      |  

That's the first problem. You didn't specify a member initializer list, but _digipot still has to be constructed, so the default is for the object's default constructor to be called, i.e. a constructor taking no arguments. But class DS3904 doesn't have such a constructor, so this is an error.

The second issue is that your statement _digipot(digipotAddress, 2); in the body of the constructor isn't parsed as an attempt to call _digipot's constructor (after all, that should be impossible, since by the time we get here it should already have been constructed). Instead, a statement of the form obj(arg, arg) is applying the function call operator operator() to obj. If obj were a function or a function pointer, this would simply call the function, but this operator can also be overloaded for other types; see Why override operator()? for some examples of where this is useful.

So the compiler looks for an overloaded DS3904::operator() taking arguments compatible with what you've passed: an int lvalue, which could be passed as a reference (int&), and an int rvalue, which would have to be passed by value. No such overload exists, so this results in your second error.

By the way, clang's error messages on this code are a bit clearer:

Cell-old.cpp:4:7: error: constructor for 'Cell' must explicitly initialize the member '_digipot' which does not have a default constructor
Cell::Cell(int cellNumber, int digipotAddress, int resistorAddress) {
      ^
./Cell.h:15:16: note: member is declared here
        DS3904 _digipot;
               ^
./DS3904.h:27:7: note: 'DS3904' declared here
class DS3904
      ^
Cell-old.cpp:5:5: error: type 'DS3904' does not provide a call operator
    _digipot(digipotAddress, 2);
    ^~~~~~~~

When faced with a confusing error message, it's often helpful to try a different compiler, and see if it can give you something more useful.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82