On 05/27/17 04:16, Alf P. Steinbach wrote:
> On the other hand, as you illustrated by having a `protected` constructor in the base class, the base class distinctions of accessibility are lost,
> because the `using` declaration works conceptually in the same way as a `using` declaration for a named base class function, just making it available
> with the accessibility that's in effect at the point of the `using` declaration.
May I disagree on this point?
See this link:
http://en.cppreference.com/w/cpp/language/using_declaration
It says:
"
Using-declaration introduces a member of a base class into the derived class definition, such as to expose a protected member of base as
public member of derived.
"
BUT the using-declaration has a different function when working with constructors. From the same link:
"
If the using-declaration refers to a constructor of a direct base of the class being defined (e.g. using Base::Base;), constructors of that
base class are inherited, according to the following rules:
[...]
All candidate inherited constructors that aren't the default constructor or the copy/move constructor and whose signatures do not match
user-defined constructors in the derived class, are implicitly declared in the derived class. <<====== ! I will use this at the end of the post !
[...]
It has the same access as the corresponding base constructor.
"
That is:
1- Using-declarate works to constructors as a possibility of inheritance. This function does not matter to other methods because the other methods are
naturally inherited when access is granted. It doesn't change the access.
2- Using-declarate works to others methods as a manner to expose a member of base class in derived class.
Let's return to initial code:
// ---------code 1----------------------
#include <iostream>
using namespace std;
class animal
{
protected:
animal()
{
cout << "Animal constructed" << endl;
}
};
struct bird: animal
{
using animal::animal;
};
int main()
{
bird x{};
return 0;
}
// ---------- end code 1 --------------
Firstly, animal has a default constructor, re-read the rule:
*** [...]All candidate inherited constructors that aren't the default constructor[...] ***
So, the code 1 is equivalent to:
// ---------code 2----------------------
#include <iostream>
using namespace std;
class animal
{
protected:
animal()
{
cout << "Animal constructed" << endl;
}
};
struct bird: animal
{
// nothing
};
int main()
{
bird x{};
return 0;
}
// ---------- end code 2 --------------
$ CC -std=c++11 test.cpp
$ ./a.out
Animal constructed
$
Let's do a little change on Code 1:
// ---------code 3----------------------
#include <iostream>
using namespace std;
class animal
{
protected:
animal(int x)
{
cout << "Animal constructed" << x << endl;
}
};
struct bird: animal
{
using animal::animal;
};
int main()
{
bird x{3};
return 0;
}
// ---------- end code 3 --------------
As we saw, the inherited constructor "has the same access as the corresponding base constructor".
So, code 3 will not work because animal constructor is *protected*.
$ CC -std=c++11 test.cpp
test.cpp:20:14: error: calling a protected constructor of class 'bird'
bird x{3};
^
test.cpp:15:23: note: implicitly declared protected here
using animal::animal;
^
1 error generated.
$
Changing from *protected* to *public*:
// ---------code 4----------------------
#include <iostream>
using namespace std;
class animal
{
public:
animal(int x)
{
cout << "Animal constructed" << x << endl;
}
};
struct bird: animal
{
using animal::animal;
};
int main()
{
bird x{3};
return 0;
}
// ---------- end code 4 --------------
$ CC -std=c++11 test.cpp
$ ./a.out
Animal constructed3
$
It works now because the animal constructor is public.
Testing if other method has the same behavior...
Lets add a function f with protected access:
// ---------code 5----------------------
#include <iostream>
using namespace std;
class animal
{
public:
animal(int x)
{
cout << "Animal constructed" << x << endl;
}
protected:
void f(int x) // function protected added
{
cout << "f hello" << x << endl;
}
};
struct bird: animal
{
using animal::animal;
using animal::f; // <--- Observe here
};
int main()
{
bird x{3};
x.f(3);
return 0;
}
// ---------- end code 5 --------------
As we saw, the code 5 will "expose a protected member of base as public member of derived."
A *Different* behavior when compared to constructors.
$ CC -std=c++11 test.cpp
$ ./a.out
Animal constructed3
f hello3
$
____________________________________________
My conclusion:
This using-declaration of the book PPP2 page 456 is wrong because it is trying "expose a protected member of base as public member of derived" through
using-declaration BUT this member is a constructor that, as the cppreference says, in this situation "has the same access as the corresponding base
constructor" (protected).