6

Say we have a class that has a private constructor, through friend we can allow some specific class(es) to still create objects of this class:

class Foo
{
 friend class Bar;
private:
  Foo();
};

class Bar
{
  Bar()
  {
    //create a Foo object
  }
};

Now what if I want the opposite of friend, where Foo looks like this:

class Foo
{
 //enemy/foe??? class Bar; (if only)
public:
  Foo();
};

And then no method of Bar can access the Foo constructor/ make an object of Foo but other classes can (because it's public).

class Bar
{
  Bar()
  {
    Foo foo; //compiler error
  }
};

Is such a construct possible or am I stuck with keeping Foo private and adding friends for all the classes?

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
  • 7
    Unfortunately no. The reason is one must keep their friends close, but their enemies even closer. ;) – NathanOliver Oct 19 '18 at 12:58
  • Why would you even want that? Sounds like your design has issues. `friend` should be used sparingly; if you have to use it for "all the classes", you did something wrong. – Sebastian Redl Oct 20 '18 at 08:23
  • @SebastianRedl That's why I dont want to take the `friend` everything but `Bar` approach, re-read the question. `Bar` shouldn't be able to access `Foo`, it's way more complicated in the real world example but I have a good reason for it. Right now, `Foo`'s constructor is public and no `friend` is used. – Hatted Rooster Oct 20 '18 at 10:57
  • "I have a good reason for it." - I'm extremely curious. – Sebastian Redl Oct 20 '18 at 11:03
  • @SebastianRedl `Bar` contains transactional logic for a microcontroller and `Foo` allows access through a legacy API to some parts of that controller. Used interchangeably can cause issues. Runtime checks are already in-place so even if it happens accidentally it won't cause a problem per se but I'd rather have a compile time error added to explain why it's not allowed. – Hatted Rooster Oct 20 '18 at 11:06
  • That sounds like you more generically want to prohibit `Foo` and `BetterFoo` from being used in parallel, not specifically disallow `Bar` from using `Foo`. (You can't do that either, but ...) – Sebastian Redl Oct 20 '18 at 11:11
  • @SebastianRedl Fair enough ^^ – Hatted Rooster Oct 20 '18 at 11:11

4 Answers4

8

Such a thing does not exist, and it would be extremely pointless. Imagine you have a situation like this:

class Foo
{
  enemy class Bar;

public:
  Foo() {}
};

class Bar
{
  void evil() { Foo{}; }
};

Nothing prevents the implementor of Bar from doing this:

class Bar
{
  void evil() { do_evil(*this); }
};

void do_evil(Bar &self)
{
  Foo{};
}

do_evil is not a member of Bar (it's a global function), and so it's not an enemy. Such non-friendliness could therefore be trivially circumvented.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 4
    Fair enough, `#define private public` is one way too though :P. `const_cast` is also a dirty hack. For me it's not about making 100% sure that no matter how hard you try you can't make a `Foo` object, it's rather about emitting a compiler error inside the methods so the client knows they're probably doing something wrong messing with `Foo` inside `Bar` and thus shouldn't try to circumvent it. – Hatted Rooster Oct 19 '18 at 12:58
  • 1
    @SombreroChicken The problem is that `#define provate public` leads to invalid code; this trivial workaround doesn't. – Angew is no longer proud of SO Oct 19 '18 at 13:04
  • 1
    @SombreroChicken - Redefining a language keyword using a macro gives undefined behaviour. – Peter Oct 19 '18 at 13:14
  • @SombreroChicken If you can modify `Bar`, you could declare a local `Foo` inside it to something nonsensical or something which will produce your desired error, so that using the name `Foo` will use the nested one and not the global one. – Angew is no longer proud of SO Oct 19 '18 at 18:27
1

It cannot be done really, but maybe following is enough for you:

template <typename T> struct Tag {};

class Foo
{
public:
    template <typename T>
    Foo(Tag<T>) {}

    Foo(Tag<Bar>) = delete;

    // ...
};

And so asking "creator" to "identify" itself.

class Bar
{
    Bar()
    {
        Foo foo{Tag<Bar>{}}; //compiler error

        // Foo foo{Tag<void>{}}; // valid as we can cheat about identity.
    }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

There is no such concept in C++.

Public attributes will always be public, but you can limit the exposure of Foo by making the constructor protected, for instance, and only visible for selected classes (although limiting friend is advised). Perhaps also make Foo as a protected class of Bar2 because only Bar2 or its children will actually use it.

Matthieu Brucher
  • 21,634
  • 7
  • 38
  • 62
0

As it has already been sayed by others, your desire breaks the idea of encapsulation because you cannot always know who your enemies are.

But, still, there is a possibility to get (almost) what you want:

#include <type_traits>

struct enemy;  // We need a forward declaration of your enemy

struct my_class {

    // The access is done using a factory, where the caller has to tell
    // us his own name
    template <class T>
    struct make{
        static_assert(!std::is_same<T,enemy>::value,"you are my enemy.");
        friend T;
    private:
        my_class operator() () { return my_class{}; }
    };


private:
    my_class(); // This is the constructor we care about

};

struct no_enemy {
    void bar() {
        my_class a = my_class::make<no_enemy>{}();  // works
    }
};


struct enemy {
    void bar() {
        my_class b = my_class::make<enemy>{}();     // error: static_assert failed
        my_class c = my_class::make<no_enemy>{}();  // error: foo is "private"
    }
};
Handy999
  • 766
  • 4
  • 8