delegating into private parts - c++

Sometimes, C++'s notion of privacy just baffles me :-)
class Foo
{
struct Bar;
Bar* p;
public:
Bar* operator->() const
{
return p;
}
};
struct Foo::Bar
{
void baz()
{
std::cout << "inside baz\n";
}
};
int main()
{
Foo::Bar b; // error: 'struct Foo::Bar' is private within this context
Foo f;
f->baz(); // fine
}
Since Foo::Bar is private, I cannot declare b in main. Yet I can call methods from Foo::Bar just fine. Why the hell is this allowed? Was that an accident or by design?
Oh wait, it gets better:
Foo f;
auto x = f.operator->(); // :-)
x->baz();
Even though I am not allowed to name the type Foo::Bar, it works just fine with auto...
Noah wrote:
type names defined within a class definition cannot be used outside their class without qualification.
Just for fun, here is how you can get at the type from outside:
#include <type_traits>
const Foo some_foo();
typedef typename std::remove_pointer<decltype( some_foo().operator->() )>::type Foo_Bar;

Trying to find anything in the standard that would spell it out in detail but I can't. The only thing I can find is 9.9:
Type names obey exactly the same scope rules as other names. In particular, type names defined within a class definition cannot be used outside their class without qualification.
Essentially, the name of Foo::Bar is private to Foo, not the definition. Thus you can use Bars outside of Foo, you just can't refer to them by type since that name is private.
The name lookup rules for members would also seem to have some effect on this. I don't see anything that specifically references "nested class" and thus they wouldn't be allowed to (if my lack of finding anything in fact is because it's not there).

I can't provide a full answer, but maybe a starting point. The C++ 1998 specification includes the following code example under paragraph 11.3 [class.access] (p. 175):
class A
{
class B { };
public:
typedef B BB;
};
void f()
{
A::BB x; // OK, typedef name A::BB is public
A::B y; // access error, A::B is private
}
In this example, a private type is "published" through a public typedef. Although it's not the same thing as publishing a type through a member function signature, it's similar.

I think this is by design. You cannot explicitly create instance of Foo::Bar but it could be returned from member functions and then you could pass it to other member functions. This lets you to hide implementation details of your class.

Related

Is there a context where the expression `a.b::c` makes sense?

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.

On the use of elaborated type specifiers to avoid hiding by a setter function

The more I program, the more I like the snake_case and the less the CamelCase. There is one particular inconvenience irritating me a bit: I do not like getters start from the get_ prefix, but that is not always possible without some extra work when using the snake_case. Consider, for instance, a getter for a member of enum type defined inside a class,
class bar
{
public:
enum class foo { a, b };
foo foo() const { return m_foo; }
void set_foo(foo new_foo) { m_foo = new_foo; }
private:
foo m_foo;
};
This won't compile since (if I understand correctly) enum foo is hidden in both the setter new_foo parameter type and the private m_foo member declaration by the prior foo() getter declaration. The obvious fix is to name the getter as get_foo, but as I said, I don't like it.
When I compile with Clang it suggests another "workaround":
error: must use 'enum' tag to refer to type 'foo' in this scope
void set_foo(foo new_foo) { m_foo = new_foo; }
^
enum
and the same for m_foo member. The following code indeed compiles fine
class bar
{
public:
enum class foo { a, b };
foo foo() const { return m_foo; }
void set_foo(enum foo new_foo) { m_foo = new_foo; }
private:
enum foo m_foo;
};
It looks like the elaborated type specifiers are even supposed to solve such kind of problems:
Elaborated type specifiers may be used to refer to a
previously-declared class name (class, struct, or union) or to a
previously-declared enum name even if the name was hidden by a
non-type declaration.
But is not that a misuse of a language feature? Have you ever seen anything like that in libraries that use snake_case, such as, for instance, the Standard Library or Boost, or write it yourself?

Why is a forward declaration in a function declaration allowed?

While reading about the visitor pattern I ran into this snippet of code:
virtual void visit(class Composite *, Component*) = 0;
This is a member function, and it seems to be forward declaring the class Composite inside its parameters. I tried this with just a normal function, like so:
void accept(class A a);
for some class A that I haven't declared or defined yet and the code worked fine. Why is this allowed? How, if at all, is it different from forward declaring before the line? Has anything changed recently in the standard in regards to this?
Many people are claiming this is a leftover of C, but then why does this code compile fine in C++, but not C?
#include <stdio.h>
int process(struct A a);
struct A{
int x;
};
int process(struct A a){
return a.x;
}
int main(void)
{
struct A a = {2};
printf("%d", process(a));
return 0;
}
This is called an incomplete type, and is a concept C++ inherited from C.
Incomplete types work this way: before you've defined a class B in your code, you can use class B varname as, say, an argument in function prototypes, or use pointers to this type as class B* ptr - anywhere where no details about a type besides its name are really needed.
Actually, you can write it differently - just put a class B; (which should work as a class declaration) before you use it as an incomplete type, and then you can write B varname instead of class B varname.
Pointers to incomplete types are often used with opaque pointers, which are probably the most common use of incomplete types in C++. Opaque pointers are described well enough in the linked Wikipedia article. Put short, it is a technique that allows your API to hide an entire class implementation.
Using the incomplete type syntaxis you describe in your question, the example code from Wikipedia:
//header file:
class Handle {
public:
/* ... */
private:
struct CheshireCat; // Not defined here
CheshireCat* smile; // Handle
};
//CPP file:
struct Handle::CheshireCat {
int a;
int b;
};
can be rewritten as:
//header file:
class Handle {
public:
/* ... */
private:
struct CheshireCat* smile; // Handle
};
//CPP file:
struct CheshireCat {
int a;
int b;
};
Note this: these code snippets are not equivalent, as the former defines a type Handle::CheshireCat, while the latter has it simply as CheshireCat.
On the code you gave as an example:
In C, the reason it doesn't compile is quite simple: the struct A in the function prototype is a declaration scoped to the function prototype, and thus it is different from the struct A which is declared latter. C and C++ have slightly different rules for this specific case. If you forward-declare the struct like this: struct A; before the function prototype, it will compile in both languages!
Other notable uses of this syntaxis:
This syntaxis has an important place as part of C++'s backward compatibility with C. You see, in C, after defining or forward-declaring a struct like this: struct A {}; or struct A;, the type's actual name would be struct A. To use the name as A, you needed to use a typedef. C++ does the latter automatically, but allows you to use A both as struct A and A. Same goes for class-es union-s, and enum-s.
Actually, some argue this has a semantical importance. Consider a function with the following signature: int asdf(A *paramname). Do you know what A is just by looking at the declaration? Is it a class, struct, enum or a union? People say that a signature like that can be made clearer in such a way: int asdf(enum A *paramname). This is a nice way of writing self-documenting code.
In C, structure names were not accessible without the struct keyword:
struct Foo {};
void bar(struct Foo foo) {...}
You could get around this by using a typedef as well:
typedef struct Foo {} Foo;
void bar(Foo foo) {...}
In C++, this remains for backward compatibility. It's been logically extended, including support for the class keyword instead of struct. class Composite * is almost equivalent to just saying Composite * in this regard. It is not necessarily used as a forward declaration, just accessing the type name.
Note that it can still be used to disambiguate if necessary:
struct Foo {};
void Foo();
int main() {
Foo foo; //error: Foo refers to the function
struct Foo foo; //okay: struct Foo refers to the class
}
Now the same declaration can introduce a type name, like it does in your accept example and possibly does in the visit example. For a function at namespace scope, if the class being declared is not found, it will be declared in the namespace containing the function (see N4140 [basic.scope.pdecl]/7).
This means that the following will not compile due to the struct/union mismatch:
void foo(struct Bar bar);
union Bar;
The above code is roughly equivalent to:
struct Bar;
void foo(Bar bar);
union Bar;

Forward declaration and friend functions

I have the following code
class foo
{
public:
foo() {}
private:
int foo_int;
friend class bar; //----->Statement A
};
class bar
{
public:
void someMethod()
{
foo f;
f.foo_int = 13;
}
};
Now I also read this answer on SO. However I cant put the pieces of puzzle together as to why
the compiler recognizes bar as a type. I was under the impression that it would complain of Bar being an incomplete type however that did not happen. My question is why ?
friend class bar;
is simply a declaration ... there's nothing for the compiler to complain about. The limitation on incomplete types is when the compiler needs information about the type that it doesn't have, such as its size or, for base classes, its members, but for friend it doesn't need anything other than its name.
Note that it doesn't matter where the friend declaration occurs in the class definition ... that it follows private: doesn't make it private. It's better to put it at the top of the class definition.
A friend specification of a class that has not been declared yet acts as a declaration of the class. It is perfectly fine to declare an incomplete type as a friend of a class because the compiler only needs to know about the type being declared.

"Using" directive fails within template

I understand that the member names of a base-class template are hidden within the scope of a derived class, and therefore must be accessed using this->foo or Base<T>::foo. However, I recall that C++ also allows you to use the using keyword, which can come in handy in a derived-class function which frequently accesses a base-class variable. So, in order to avoid cluttering up the function with this-> everywhere, I'd like to use the using keyword.
I know I've done this before, but for whatever reason I can't get it to work now. I'm probably just doing something stupid, but the following code won't compile:
template <class T>
struct Base
{
int x;
};
template <class T>
struct Derived : public Base<T>
{
void dosomething()
{
using Base<T>::x; // gives compiler error
x = 0;
}
};
int main()
{
Derived<int> d;
}
The error, (with GCC 4.3) is: error: ‘Base<T>’ is not a namespace
Why doesn't this work?
It doesn't work because C++ language has no such feature and never had. A using-declaration for a class member must be a member declaration. This means that you can only use in class scope, but never in local scope. This all has absolutely nothing to do with templates.
In other words, you can place your using-declaration into class scope
struct Derived : public Base<T> {
...
using Base<T>::x;
...
};
but you can't have it inside a function.
Using-declarations for namespace members can be placed in local scope, but using-declarations for class members cannot be. This is why the error message complains about Base<T> not being a namespace.
Outside class scope (if you are in a block etc), you can only name namespace members in a using declaration.
If you don't want to place that using declaration into the scope of Derived (which IMO is the favorable solution), your other option is to use a reference
int &x = this->x;
x = 0;
It should be noted that this is semantically different, because it
Forces a definition to exist for Base<T>::x, which might not be the case for static const data members
Forces Base<T>::x to be of type int& or be convertible to type int&.
Otherwise, if you want to avoid using this-> all again, I don't see other options.
template <class T>
struct Base
{
int x;
};
template <class T>
struct Derived : public Base<T>
{
using Base<T>::x;
void dosomething()
{
x = 0;
}
};
int main()
{
Derived<int> d;
}
As others said, it is working only class scope.