Напротив объявления друга - PullRequest
0 голосов
/ 19 октября 2018

Скажем, у нас есть класс с закрытым конструктором. Через friend мы можем разрешить некоторому определенному классу (классам) создавать объекты этого класса:

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

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

А что еслиЯ хочу противоположность friend, где Foo выглядит так:

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

И тогда никакой метод Bar не может получить доступ к конструктору Foo / создать объект Foo, нодругие классы могут (потому что это public).

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

Возможна ли такая конструкция, или я застрял с сохранением Foo закрытым и добавлением друзей для всех классов?

Ответы [ 4 ]

0 голосов
/ 20 октября 2018

Как уже было сказано другими, ваше желание разрушает идею инкапсуляции, потому что вы не всегда можете знать, кто ваши враги.

Но, тем не менее, есть возможность получить (почти) то, что выхочу:

#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"
    }
};
0 голосов
/ 19 октября 2018

В C ++ такого понятия нет.

Открытые атрибуты всегда будут публичными, но вы можете ограничить экспозицию Foo, сделав конструктор защищенным, например, и видимым только для выбранных классов (хотя ограничение friend рекомендуется).Возможно, также сделайте Foo защищенным классом Bar2, потому что только Bar2 или его дочерние элементы будут фактически использовать его.

0 голосов
/ 19 октября 2018

Это не может быть сделано на самом деле, но, возможно, вам достаточно следующего:

template <typename T> struct Tag {};

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

    Foo(Tag<Bar>) = delete;

    // ...
};

И поэтому просите «создателя» «идентифицировать» себя.

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

        // Foo foo{Tag<void>{}}; // valid as we can cheat about identity.
    }
};
0 голосов
/ 19 октября 2018

Такого не существует, и это было бы крайне бессмысленно.Представьте, что у вас есть такая ситуация:

class Foo
{
  enemy class Bar;

public:
  Foo() {}
};

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

Ничто не мешает разработчику Bar сделать это:

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

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

do_evil не является членом Bar (это глобальная функция), и поэтому это не враг.Поэтому такое недружелюбие можно обойти тривиально.

...