I made this simple class, which still is playing with my mind:
class A {
private:
class B {};
public:
B getB() {
return B();
};
};
As of C++03, this class compiles fine, but there is just no nice looking way to assign the result of getB() to an lvalue, in the sense that:
A::B b = A().getB();
Does not compile.
I got it by using an intermediate template, in this fashion:
template <typename T>
struct HideType {
typedef T type;
};
HideType<A::B>::type b = A().getB();
But this looks just terrible, for this simple task of getting an A::B lvalue variable.
This is not true anymore as of C++11, or at least it is not with gcc. This code is still not valid:
A::B b = A().getB();
But this is valid:
auto b = A().getB();
Is there a loophole in the standard respect to this?
From Standard, Clause 11 (Member access control):
A member of a class can be
— private; that is, its name can be used only by members and friends of the class in which it is declared.
— protected; that is, its name can be used only by members and friends of the class in which it is
declared, by classes derived from that class, and by their friends (see 11.4).
— public; that is, its name can be used anywhere without access restriction.
So access control is applied to names.
In
auto b = A().getB();
you don't use private names, therefore it's legal, according to Standard
The fact why A::B b = A().getB() doesn't work is because B is a private class member of A. If you make it public then your code will compile. It works with auto because auto just checks the type of the object assigned to it without needing to call the constructor of the object (just like declval). So it assigns b with the return type of getB present in class A. You can change your code in the following way too :
decltype( declval<A>().getB() ) b = A().getB();
( If declval is new to you then I must tell you that it would return an rvalue of the return type of the function (here getB whose return type is B) of a class (here A) without calling the constructor of the class ! (This should however be used with functions like decltype and sizeof only.) So it prevents the overhead of creating a class object and then using its function. )
Now according to me, I don't think this is a loophole in the standard rather I feel the loophole has been removed ! It's obvious from your own code, see the function getB. How difficult it is to instantiate an object of B because it's defined in private ! In such a case working with B becomes difficult. The idea behind this is that the name of the type (ie B) in the class A is inaccessible but the type is still usable which is why you can get an object of B. You can understand the use of auto if you understand the this template code :-
#include <iostream>
#include <type_traits> // for std::is_same
using namespace std;
class A
{
class B
{};
public:
B getB()
{
return B();
}
};
template<typename T>
void check (T b)
{
cout<<boolalpha;
is_same<decltype( declval<A>().getB() ), T> x; // checks if T & B are of same type
cout<<x.value<<'\n';
}
int main()
{
A obj;
check (obj.getB());
return 0;
}
Output :-
true
As the template could identify B, hence auto too identifies B.
It seems, there was such a defect in a draft of the Standard, but it had been corrected by WP 1170.
Probably, there is a compiler bug. The declaration auto b = A().getB(); involves template argument deduction for the auto type-specifier, so according to the C++11 Standard it should be ill-formed, because that type deduction fails.
Related
Consider this code:
class A
{
struct B {};
std::vector<B> _vec;
public:
const std::vector<B>& get() const { return _vec; }
};
Note that B is private within class A. The above code compiles, but when calling get() from outside class A, it does not:
const std::vector<A::B>& vec = get(); // does not compile
Indeed, A::B is private. However, from C++11 on, you could simply do this:
const auto& vec = get();
which works perfectly.
For the same reason as above, you cannot do the following:
A::B obj;
But, since there is a public getter, you can deduce the type from that getter function. In this particular case, one could do:
using B = std::decay<decltype(C{}.get())>::type::value_type;
B obj;
Questions
I'm not sure how to formulate my questions. It seems strange to me that, from C++11 on (and not before), we actually can instantiate A::B being the latter private. And even more, I think it's strange we can call const auto& get(). Is there any explanation for this? Wouldn't it be better not be allowed doing this? Should I declare A::B public? I feel like it doesn't make sense to declare it private if you need a getter function like the one in the above code.
It seems strange to me that, from C++11 on (and not before), we actually can instantiate A::B being the latter private.
You could do it in C++98/03 just fine, through template argument deduction:
template<typename T>
void temp_func(const T &t)
{
...
T u = t;
}
temp_func(a.get()); //Will use the private type.
You can even use T inside of temp_func.
Making a type private was never a guarantee of making the type inaccessible from the outside. Private has always refer to the accessibility of the name, not of the construct behind the name. If you want a type to only be used within a scope, then that typename cannot be part of any non-private interfaces. And this has always been the case.
Should I declare A::B public?
That's up to you. However, if you expose a public user interface, you are making a statement that the user can, you know, use the interface. In order for the user to know how to use a vector<A::B>, they have to know how A::B behaves. So they have to use its definition. Even though it's private.
So if a user has to know about a type and about how a type behaves... is it really "private"?
When you mark something private, it does not mean unaccessible. It simply mean that the declaration cannot be accessed from the outside.
For example, this will work:
class A {
struct B {};
public:
using C = B;
};
auto main() -> int {
auto c = A::C{};
}
Here I access B, because I used the public alias to it.
Same thing for private members:
class A {
int i;
public:
auto the_i() -> int A::* {
return &A::i;
}
};
auto main() -> int {
auto a = A{};
auto member = a.the_i();
// Whoa! I access the member directly! Shouldn't this prohibited?
a.*member = 9;
}
The answer is no. The declaration is private, but can still be accessed by other means.
Same thing with member types, expose it in the public interface and users will be able to use it.
If you don't want users to use the private type, simply don't expose it in the public interface.
In C++ consider the grammar rule:
member-access-expression: LHS member-access-operator RHS
(op is .)
and
LHS=unqualified id-expression e.g. that references an instance variable.
RHS=qualified id-expression (with at least one nested identifier)
example: a.b::c
If that can ever pass the semantic check, what situation would it be ?
The following experiment:
struct B{};
struct A
{
B b;
};
int main()
{
A a;
a.b::c;
}
returns
'b' is not a class, namespace, or enumeration
a.b::c;
^
(demo)
This tends to hint to me that there can't be any legal case of a qualified-id on the right of a member access.
A very simple example is if you want to call a member function of a parent class:
struct A {
void f();
};
struct B: A {
void f();
};
B b;
b.A::f();
One use case is accessing members of an enum within some struct A by using an instance of A (rather than using the enum directly via A::b::c):
struct A {
enum class b { c }; // can be unscoped as well
};
A a;
a.b::c; // Access to enum value c - similarly, A::b::c would work
Here's a trivial example:
struct A {
void f() {}
};
int main()
{
A a;
a.A::f();
}
A::f() is a qualified version of the name for the function f that's a member of A. You can use it in member access just like the "short" (or unqualified) name.
In fact, one might argue that every time you write a.f(), that's a shortcut for a.A::f() (with the A:: part being taken automatically from decltype(a)).
There's nothing magic about this, though it's unusual to see the construct outside of the sort of scenarios the other answerers demonstrated, because in this example it's redundant.
I have a class template whose constructor accepts a callable whose type is a template parameter. I would like to have that type deduced so I don't have to specify it whenever instantiating the class.
Unfortunately, the type deduction doesn't work in the example below. Is there a way to get it to work?
template<typename F>
class C {
public:
C(F&& f) : m_f{f} {}
private:
F m_f;
};
class D {
public:
static int s() { return 0; }
private:
C<decltype(&s)> c {&s}; // OK
C<> c2 {&s}; // error, not enough template parameters
};
https://wandbox.org/permlink/8cphYR7lCvBA8ro4
Note this is similar to Can template parameter deduction be used in class data members? but here I'm asking about getting something similar to work, not about standard compliance.
One more note is that while re-specifiying the type of the template parameter in the example above is just a non-DRY inconvenience (which one of the answers below suggests solving with a macro), I'm not sure how it would be possible to have an instance of C with F being a non-global lambda function type (e.g. one that's defined on the spot), in case that instance is a data member. A technique which would allow that would be very powerful and useful, IMHO.
If your main goal is to avoid typing &s twice, the pragmatic solution is to define a macro:
#define CC(name,value) decltype(C{value}) name{value}
class D {
public:
static int s() { return 0; }
private:
CC(c,&s);
// lambda still not possible:
// CC(c2,[](){return 42;});
};
You could do something like this:
decltype(C{&s}) c{&s};
But I'm not aware of a way to avoid duplicating the &s.
I made this simple class, which still is playing with my mind:
class A {
private:
class B {};
public:
B getB() {
return B();
};
};
As of C++03, this class compiles fine, but there is just no nice looking way to assign the result of getB() to an lvalue, in the sense that:
A::B b = A().getB();
Does not compile.
I got it by using an intermediate template, in this fashion:
template <typename T>
struct HideType {
typedef T type;
};
HideType<A::B>::type b = A().getB();
But this looks just terrible, for this simple task of getting an A::B lvalue variable.
This is not true anymore as of C++11, or at least it is not with gcc. This code is still not valid:
A::B b = A().getB();
But this is valid:
auto b = A().getB();
Is there a loophole in the standard respect to this?
From Standard, Clause 11 (Member access control):
A member of a class can be
— private; that is, its name can be used only by members and friends of the class in which it is declared.
— protected; that is, its name can be used only by members and friends of the class in which it is
declared, by classes derived from that class, and by their friends (see 11.4).
— public; that is, its name can be used anywhere without access restriction.
So access control is applied to names.
In
auto b = A().getB();
you don't use private names, therefore it's legal, according to Standard
The fact why A::B b = A().getB() doesn't work is because B is a private class member of A. If you make it public then your code will compile. It works with auto because auto just checks the type of the object assigned to it without needing to call the constructor of the object (just like declval). So it assigns b with the return type of getB present in class A. You can change your code in the following way too :
decltype( declval<A>().getB() ) b = A().getB();
( If declval is new to you then I must tell you that it would return an rvalue of the return type of the function (here getB whose return type is B) of a class (here A) without calling the constructor of the class ! (This should however be used with functions like decltype and sizeof only.) So it prevents the overhead of creating a class object and then using its function. )
Now according to me, I don't think this is a loophole in the standard rather I feel the loophole has been removed ! It's obvious from your own code, see the function getB. How difficult it is to instantiate an object of B because it's defined in private ! In such a case working with B becomes difficult. The idea behind this is that the name of the type (ie B) in the class A is inaccessible but the type is still usable which is why you can get an object of B. You can understand the use of auto if you understand the this template code :-
#include <iostream>
#include <type_traits> // for std::is_same
using namespace std;
class A
{
class B
{};
public:
B getB()
{
return B();
}
};
template<typename T>
void check (T b)
{
cout<<boolalpha;
is_same<decltype( declval<A>().getB() ), T> x; // checks if T & B are of same type
cout<<x.value<<'\n';
}
int main()
{
A obj;
check (obj.getB());
return 0;
}
Output :-
true
As the template could identify B, hence auto too identifies B.
It seems, there was such a defect in a draft of the Standard, but it had been corrected by WP 1170.
Probably, there is a compiler bug. The declaration auto b = A().getB(); involves template argument deduction for the auto type-specifier, so according to the C++11 Standard it should be ill-formed, because that type deduction fails.
Here is some code that fails in VS2012:
class A;
class B
{
bool A();
A member; // Error: function B::A is not a type name
};
Why doesn't this work? Obviously I'm trying to create a member of type A, not of type B:A() (which isn't a type, as the compiler correctly points out). Is there some way around this without changing the names of either B:A() or class A? Can I explicitly tell the compiler that I want member to be of type class A?
It's possible, you just need to make clear that you want to use the class A:
class A;
class B
{
bool A();
class A member;
};
It fails because they share a namespace. C++ cannot, in general distinguish types and functions based on context (is A() a constructor or a function call). You can make it explicit which one you refer to, e.g.
class A;
class B
{
bool A();
::A member;
};