25

I have been playing around with inner classes in C++, and I'm bit confused right now.

My Code:

#include <iostream>

class outer{
private: 
    class inner{
    private:
        int something;
    public:
        void print(){
            std::cout<< "i am inner"<<std::endl;
        }   
    };
public:
    inner returnInner(){
        inner i;
        return i;
    }  
};

int main(){
    outer o;
    //outer::inner i = o.returnInner(); (1)
    //auto i = o.returnInner();         (2)
    //i.print();                  
    o.returnInner().print();          //(3)
    return 0;
}

This is compiled on Linux with clang++-3.5 and -std=c++14.

  • In (1) I'm getting a compiler error as expected because inner is a private inner class of outer.

  • However, in (2) when the auto keyword is used, compilation is successful and the program runs.

  • Everything works for (3), too.

My question:

Why is this so? Can I prevent returning an instance of private inner class from outer methods while retaining the ability to move and/or copy them within the outer class?

Shouldn't the compiler elicit an error in (2) and (3) like it does in (1)?

Community
  • 1
  • 1
Kuna Prime
  • 265
  • 3
  • 8

2 Answers2

30

why is it so Shouldn't compiler return error in (2) and (3) as in (1)?

It is an interesting question.. and answer to this is even more interesting.

In the code you posted, inneris a name of a type. It is the name which is declared to be private, not the type itself1 — accessibility of type doesn't make sense in the context of C++; accessibilty applies to name only — be it name of type, name of function, name of data . So when you hear something like "the type X is a public class", it almost always means "the name X is declared to be public, and it refers to a type and the type is omnipresent, it exists everywhere".

Coming back to why using auto doesn't give error, well because when you use auto, you're accessing the type without using its name, which is why it doesn't give error.

how can i prevent return of an instance of private inner class from outer methods,

There is no way.


1. struct { int data; } is a type, without a name to refer to, though note that is not a valid declaration, because it lacks name. Either you've to declare an object of unnamed type as in struct { int data; } obj; Or give it a name as struct data_type { int data; };

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 2
    Ability to use something without referring to its name is nothing special. One can return a reference to a private member variable and then use it as one sees fit. Very few people are surprised by that (though some still are). – n. m. could be an AI Jul 02 '15 at 13:13
  • 3
    @Nawaz thank you for you answer, this is very interesting property of c++ that i had no idea about, and also it seems to me that this complicates type safety/locality when building APIs, – Kuna Prime Jul 02 '15 at 13:16
  • @n.m. for me, surprising thing is not that one can use something without referring to it's name, for me in this case is inability to enforce compile time error where it would be intuitive (at least for me) – Kuna Prime Jul 02 '15 at 13:23
  • Why do you expect a compiler error here, and no compiler error when you say `return &private_member_variable;` ? – n. m. could be an AI Jul 02 '15 at 13:31
  • 5
    Interesting. By the way if I declare a 'typedef inner inner_public ;' in the public section then I can instantiate a variable of type 'outer::inner_public' – marom Jul 02 '15 at 13:34
  • 2
    @marom, and even cheekier, use `typedef decltype(i) inner_t;` to give a name to the type of the `auto` variable. – Aaron McDaid Jul 02 '15 at 13:43
  • @n.m. i don't have expectations from return statement (or it to cause any problems), what i'm somewhat surprised is that "private" type can be used outside defining scope. – Kuna Prime Jul 02 '15 at 13:58
  • @marom and AaronMcDaid thank you for sharing those "tricks" – Kuna Prime Jul 02 '15 at 14:01
  • Why a private type outside its scope surprises you and a private variable outside its scope doesn't? – n. m. could be an AI Jul 02 '15 at 15:59
  • 2
    @n.m.: I guess it is because when programmers talk about variable, they also talk about memory where the variable is created. And it is implicitly understood that only the variable can be `public`, `protected` or `private`; the memory where the variable actually resides (or rather *represents*), is accessible to all, through other means, and it doesn't make sense to say memory is `private`. But in case of *type* and their *name(s)*, there is no/rare similar talk that explains them. Also, people know *less* about type, compared to *data*, even though *type* **is** data, for certain programs. – Nawaz Jul 02 '15 at 16:35
  • @n.m. this is how i see it: variable is coupling between data (memory, or "instance") and type (how it should be used/seen). So when you are returning `&private_member` pointer of _member type_ on some part of memory is returned(you are returning typed "handle" to memory not variable). So far everything is fine, but when receiving scope (some outer scope) is using _member type_ which is declared as private that was counter-intuitive as i have not realized that type can not be declared as private, only type name can. In other words only names/variables can have scope not memory or types. – Kuna Prime Jul 02 '15 at 17:30
  • 1
    *variable is coupling between data (memory, or "instance") and type*. This is not really the case. Not languages are statically typed. Even dynamically typed languages have variables, and some of them even have private variables. More accurately, a variable is a name for an object. In C++ variable type is known at compile time but this is irrelevant for this discussion. So we see that a name can be declared private, but an object cannot. The situation is completely parallel to that of member types. – n. m. could be an AI Jul 03 '15 at 04:57
1

I'm not sure that it's what you want but you can delete the copy constructor like that:

inner(inner const&) = delete;