Как создать конструктор параметров шаблона для друга?

В C++ 11 они позволили дружить с параметром шаблона просто с friend T Вы также можете использовать методы friend в этом параметре с помощью friend T::Method().

Однако как вы поддерживаете конструктор параметров шаблона?

class BeMyFriend
{
public:
 BeMyFriend& operator=(const BeMyFriend& rhs) = default;
 BeMyFriend(const BeMyFriend& rhs) = default;
};

template<class T>
class Test
{
 friend T& T::operator=(const T&); //Works fine, no error
 friend T::T(const T&); //error: prototype for 'BeMyFriend::BeMyFriend(const BeMyFriend&)' does not match any in class 'BeMyFriend'
};

int main()
{
 Test<BeMyFriend> hmm;

 return 0;
}

Я могу дружить с параметром параметра template operator= просто отлично, но я не могу друг T::T(const T&).

Как я могу сделать friend T::T(const T&); Работа?


edit: Это, по-видимому, другая проблема, чем то, что решается в Make Friend конструктором класса шаблона. Проблема в работе с параметрами круглого шаблона в декларации. Он не имеет отношения к конструктору фактического параметра шаблона.

Тип Foo - это нормальный шаблонный класс, а не шаблонный параметр, такой как T в моем примере. Что-то вроде friend Foo<B>::Foo<B>() из этого представления должно компилироваться просто отлично, в отличие от вопроса, который у меня есть с friend T::T(const T&).

edit: В случае, если это в конечном итоге имеет значение, я компилирую с gcc 7.2.

edit: Я также хочу пояснить, что C++ поддерживает создание друзей-конструкторов. Например, friend X::X(char), X::~X(); в первом примере на http://en.cppreference.com/w/cpp/language/friend.

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

Ответ 1

Я смог получить его для компиляции в GCC, добавив void после friend:

friend void T::T(const T&);

Затем я смог получить доступ к одному из Test частных членов из конструктора BeMyFriend. Однако обратите внимание, что это специфический компилятор. Я пробовал это в clang, и это не сработало.

Ответ 2

Как бы то ни было, я думаю, что T::T(const T&) недостаточно разбирается в компиляторе, потому что он не рассматривает пространство имен T:: перед конструктором-членом T() как ссылку на некоторый внешний класс, очевидно, видимый в консоли ошибок ISO C++ forbids declaration of 'T with no type и prototype for 'BeMyFriend::BeMyFriend(const BeMyFriend&) does not match any in class 'BeMyFriend где компилятор явно пытается получить любое определение или декларацию, экспортированные извне класса, таким образом, T:: должен быть принудительно введен пользователем для компилятора, связанного с подружившимся классом, так что T&:: этого достаточно, чтобы устранить двусмысленность.

Вы можете проверить здесь, что программа-исполнитель "работает" отлично, и ценность правильно подружилась.

Если, тем не менее, вы видите в этом примере приведенную ошибку std::__cxx11::string Test<BeMyFriend>::mything is private within this context areyoumyfriend.mything; отображает статус нарушения доступа к частной стоимости, это связано с тем, что функция-член просто не подружилась с хост-классом.

Ответ 3

Я думаю, что C++ стандарт запрещает то, что вы пытаетесь сделать. Возможно, это дефект/надзор; возможно, это было намеренно. Я не уверен, поэтому я просто выложу фрагменты, которые я нашел в стандарте (безопасно обходить номера разделов, если/пока кто-то не проверяет мой анализ):

  1. В разделе 6.4.3.1.2 есть описание того, когда считается, что что-то называется конструктором. Это включает в себя "имя введенного класса", которое (согласно 12.2) означает имя класса, вставленного в область класса. В вашем контексте я читал эти разделы, говоря, что для обозначения конструктора вам нужно T:: за которым следует имя класса T У вас есть T::T, который работает, если T считается классом T Посмотрим, как это происходит.

  2. В 15.1.1 указано, что объявление вашего friend должно было бы назвать конструктор. Далее указывается, что имя класса не является типом-именем. Поэтому мы должны быть в порядке, пока T не является типед-именем.

  3. В 17.1.3 указано, что в вашем class Test идентификатор T является typedef-name. Ой-ой.

Так что это странно. Если я правильно читаю, вам нужно будет использовать friend T::BeMyFriend чтобы назвать конструктор, который, конечно, работает только в этом конкретном примере. Для других параметров шаблона это будет искать функцию-член под названием "BeMyFriend". Это не то что вы ищете.

(Возможно, вы заметили, что в примерах конструкторов, объявленных как друзья, используемое имя класса всегда является именем, используемым при определении класса. В этой ситуации не происходит typedef'ing, поэтому этот вопрос не возникает.)

Решение? Я думаю, вам нужно сделать класс T другом, сделать статическую функцию в T другом и вызвать это из конструктора или найти способ (лучше?) Делать то, что вы хотите сделать, не используя друзей. Когда я вижу, что параметр шаблона сделал друга - он легален, но часто идет вразрез с принципами инкапсуляции.

Ответ 4

Конструкторы - очень специальные методы. У них нет типа возврата (даже не пусто) и нет настоящего имени. AFAIK, который просто не может использоваться в декларации друзей.

Возможным обходным BeMyFriend является создание фабричной функции копирования в классе BeMyFriend:

class BeMyFriend
{
public:
 BeMyFriend& operator=(const BeMyFriend& rhs) = default;
 BeMyFriend(const BeMyFriend& rhs) = default;
 static BeMyFriend makeCopy(const BeMyFriend& rhs) {
    BeMyFriend tmp(rhs);
    return tmp;
 }
};

template<class T>
class Test
{
 friend T& T::operator=(const T&); //Works fine, no error
 //friend T::T(const T&); //error: prototype for 'BeMyFriend::BeMyFriend(const BeMyFriend&)' does not match any in class 'BeMyFriend'
 friend T T::makeCopy(const T&);
};

int main()
{
 Test<BeMyFriend> hmm;

 return 0;
}

Это на самом деле не отвечает на ваш вопрос, потому что построение копии по умолчанию не было бы другом, а в следующем коде:

BeMyFriend foo;
BeMyFriend bar = BeMyFriend::makeCopy(foo);

вы получаете копию копий друга внутри makeCopy, а следующая, скорее всего, будет отменена.

Во всяком случае, я не могу представить себе реальный случай использования для дружбы только определенного конструктора из класса, а не класса whode...