Default argument allowing constructor to call private method - c++

I have the class
class A
{
public:
class Key
{
Key() {}
Key(Key const &) {}
};
A(Key key, int a = 5) {}
};
The constructor for Key is private, so no one should be able to construct an object A. However, with the following code:
int main() {
A a(A::Key()); // this compiles !!!
A a2(A::Key(), 5); // this doesn't
// somehow defaulting the argument causes the private constructor
// to be OK - no idea why
return 0;
}
By making use of the default argument for int a in my constructor, the compiler happily compiles my usage of A::Key() despite the fact that it is private. If I explicitly give a value for a, though, the compiler correctly recognizes that I am trying to use a private constructor and errors out. Why is this? Is there someway to force the compiler to error out for the first example as well?
See here for live example.

This is because of the most vexing parse.
A a(A::Key());
Does not create a A named a and construct it with a temporary A::Key. It creates a function a that returns an A and takes an unnamed pointer to function that returns a A::Key.
If you add a pair of parentheses to it you will get a compiler error
A a((A::Key()));
That you are trying to call a private constructor. Alternatively you can use uniformed initialization which also disambiguate it and will cause a compile error
A a(A::Key{});

Related

Initialise unique_ptr inside class

I want to initialise the unique pointer inside class after declaration and I tried few ways but unable to resolve the errors..
template <typename T>
struct Destroy
{
void operator()(T *t) const
{
t->destroy();
}
};
class Test
{
std::unique_ptr<IRuntime, Destroy<IRuntime>> runtime;
public:
Test()
{
/*
the function createIRuntime() return type is *IRuntime.
I tried using following but all the ways I got error:
1. runtime = std::make_unique<IRuntime, Destroy<IRuntime>> (createIRuntime());
2. runtime = createIRuntime();
3. runtime = std::unique_ptr<IRuntime, Destroy<IRuntime>> (createIRuntime());
Works fine if I do follow.
std::unique_ptr<IRuntime, Destroy<IRuntime>> runtime(createIRuntime());
*/
/* how to initialize the unique pointer here*/
}
};
runtime = std::make_unique<IRuntime, Destroy<IRuntime>> (createIRuntime());
Presumably IRuntime is an abstract class, which can't be constructed directly.
But even if it could be constructed as-is, only the 1st template parameter specifies the type to create. The 2nd and subsequent template parameters specify the types of parameters for the constructor that is called.
So, this statement is trying to call an IRuntime constructor that takes a Destroy<IRuntime> object as a parameter, passing a raw IRuntime* pointer to that parameter. No such constructor exists, so this fails to compile.
runtime = createIRuntime();
std::unique_ptr does not have an operator= that takes a raw pointer, only a std::unique_ptr. std::unique_ptr has a constructor that takes a raw pointer, but that constructor is marked explicit. So this fails to compile, too.
runtime = std::unique_ptr<IRuntime, Destroy<IRuntime>> (createIRuntime());
This is correct, and works just fine:
Online Demo
Another statement that works is:
runtime.reset(createIRuntime());
Online Demo
Also, since the code you showed is inside of another constructor, you can (and should) use that constructor's member initialization list:
Test() : runtime(createIRuntime())
{
}
Online Demo

C++ int pointer retroactively dereferences self

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;
}
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.

C++ constructor with declaration account(int =0);

class Act {
protected:
string Owner;
double Balance;
public:
explicit Act(int = 0);
double getBalance() { return Balance; };
};
What is the meaning of line of constructor Act(int =0); Need what int=0 would do here.
Explanation
explicit Act (int = 0);
defines a constructor, that construct an Act from an int parameter. The =0 means that if the parameter can be omitted, it will have a default value of 0. The explicit keyword tells the compiler not to use this constructor for making an implicit conversion.
Examples of use
As it is:
Act a1; // Will generate the same code as Act a1(0);
Act a5{}; // Same as above, but using the braced initialization
Act a2(12); // Obvious
Act a3=13; // Ouch ! Compiler error because of explicit
Act a4 = Act(13); // Ok, because now the call is explicit
If you wouldn't have the explicit keyword, then this line would be ok
Act a3=13; // If not explicit, this is the same than Act a3=Act(13);
Important remarks
The default value is not something that is part of the constructor itself, but a behavior that is defined on the caller side, based on the declaration of the constructor known by the caller.
This means that you could include declare the class with different default values in different compilation units. Although strange, this is perfectly valid.
Note that the absence of parameter name in the declaration is not a problem either, because, the parameter name can be declared within the constructor definition:
Act::Act(int x) : Balance((double)x) {
cout <<"Constructor Act with parameter "<<x<<endl;
}
Finally, note that if you want to use the default value by omitting the parameter, but that your constructor has only one parameter, you should either use the syntax form a1 or a5 in the examples above. You should however not use the syntax with empty parentheses because this would be understood as a function declaration:
Act a0(); // Ouch !! function declaration ! Use a0 or a0{} instead.
In order to address your question we must break down what the line does.
The line calls the constructor for the class Act, the int which has no variable name requires the constructor to take an int. However the =0 part is the default parameter telling the constructor that you don't need the int just place a 0 there.

Can't initialize field outside initializer list

I'm having trouble with something that seems very easy, so I must be overlooking something.
I need to construct a class that has a field that is also a class (non-POD). The class of the field has a default constructor and a "real" constructor. The thing is that I really can't construct the field in the initializer list, because in reality the constructor has a parameter that is a vector which needs a somewhat complex for loop to fill.
Here is a minimal example that reproduces the problem.
ConstructorsTest.h:
class SomeProperty {
public:
SomeProperty(int param1); //Ordinary constructor.
SomeProperty(); //Default constructor.
int param1;
};
class ConstructorsTest {
ConstructorsTest();
SomeProperty the_property;
};
ConstructorsTest.cpp:
#include "ConstructorsTest.h"
ConstructorsTest::ConstructorsTest() {
the_property(4);
}
SomeProperty::SomeProperty(int param1) : param1(param1) {}
SomeProperty::SomeProperty() : param1(0) {} //Default constructor, doesn't matter.
But this gives a compile error:
ConstructorsTest.cpp: In constructor 'ConstructorsTest::ConstructorsTest()':
ConstructorsTest.cpp:4:19: error: no match for call to '(SomeProperty) (int)'
the_property(4);
^
It gives no suggestions like it usually would of what functions could have been intended instead.
In the above example I would just initialize the_property in the initializer list, but in reality the 4 is actually a complex vector that needs to be generated first, so I really can't. Moving the_property(4) to the initializer list causes the compilation to succeed.
Other similar threads mention that the object must have a default constructor, or that it can't be const. Both requirements seem to have been met, here.
You can't initialize data member inside the constructor's body. (the_property(4); is just trying to invoke the_property as a functor.) You can only assign them like:
ConstructorsTest::ConstructorsTest() {
the_property = ...;
}
but in reality the 4 is actually a complex vector that needs to be generated first
You can add a member function which generate the necessary data, and use it to initialize the data member in member initializer list. e.g.
class ConstructorsTest {
...
static int generateData();
};
int ConstructorsTest::generateData() {
return ...;
}
ConstructorsTest::ConstructorsTest() : the_property(generateData()) {
}
You cannot initialize a variable twice.1 When your constructor has started, all member subobjects will have been constructed. If you do not provide a member initializer in the constructor, or a default member initializer in the class definition, then it will perform default initialization. Regardless of what form it takes, you can't construct it again.
Complex multi-statement initialization is best done via a lambda function:
ConstructorsTest::ConstructorsTest()
: the_property( []{ /* Do Complex Initialization */}() )
{
}
1: Well... you can, but not like that. And you really shouldn't for cases as simple as this.

why C++ Initialization list is before brace?

I want to know what's difference in the following two class.
example 1:
class A
{
string name;
public:
A(const char* _name):name(_name){}
void print(){cout<<"A's name:"<<name<<endl;}
};
example 2:
class A
{
string name;
public:
A(const char* _name){name(_name);}
void print(){cout<<"A's name:"<<name<<endl;}}
why the example 1 is passed and the last one is wrong?
Thanks
In example 1 you initialize the string with the given value right away.
In example 2 you create an empty string first and assign it later on.
Despite some performance differences and ignoring possible differences due to copy constructor handling etc. it's essentially the same result.
However once you use a const member you'll have to use example 1's way to do it, e.g. I usually create unique IDs the following way:
class SomeObject
{
static unsigned int nextID = 0;
const unsigned int ID;
SomeObject() : ID(nextID++)
{
// you can't change ID here anymore due to it being const
}
}
The first example is an actual initialization. It has a number of advantages, including being the only way to set up const members, and having proper exception-safety.
The second example is not valid C++, AFAIK. If you had instead written name = name_, then this would just be normal assignment. Of course, this isn't always possible; the object might be const, or not have an assignment operator defined. This approach could also be less efficient that the first example, because the object is both default-initialized and assigned.
As for why the initializer list is before the constructor body; well, that's just the way the language has been defined.
That's just how the language is defined. The member initializers should be placed before the body of the constructor.
In the first example the member name is initialized with a ctr getting char * as parameter.
In the second case it is initialized with a default ctr at first and it gets value by the assignment operator (operator=) later. That's why it is wrong with your case that it is already constructed there so you can not use the ctr once again you could just use the assignment operator.
The reason is that name lookup works different in initializer lists and function bodies:
class A
{
std::string foo; // member name
public:
A(const char* foo) // argument name
: foo(foo) // member foo, followed by argument foo.
{
std::cout << foo; // argument foo.
}
};
If the initializer list was inside the function body, there would be an ambiguity between member foo and argument foo.
The motivation behind the initialization list is due to const field holding a object by value (as opposed to reference/pointer field).
Such fields must be initialized exactly once (due to their const-ness). If C++ didn't have initialization list then a ctor would look something like:
class A {
public:
const string s;
const string t;
A() {
// Access to either s or t is not allowed - they weren't initialized
s = "some-string";
// now you can access s but you can't access t
f(*this);
t = "some other string";
// From now you can access both ...
}
}
void f(A& a) {
// Can you access a.s or a.t?
cout << a.s << a.t;
}
Without an initialization list a ctor can pass a partially-initialized object of type A to a function, and that function will have no way of knowing which fields are initialized yet. Too risky and very difficult for the compiler/linker to check.