Как создать конструктор параметров шаблона для друга?
В 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++ стандарт запрещает то, что вы пытаетесь сделать. Возможно, это дефект/надзор; возможно, это было намеренно. Я не уверен, поэтому я просто выложу фрагменты, которые я нашел в стандарте (безопасно обходить номера разделов, если/пока кто-то не проверяет мой анализ):
-
В разделе 6.4.3.1.2 есть описание того, когда считается, что что-то называется конструктором. Это включает в себя "имя введенного класса", которое (согласно 12.2) означает имя класса, вставленного в область класса. В вашем контексте я читал эти разделы, говоря, что для обозначения конструктора вам нужно T::
за которым следует имя класса T
У вас есть T::T
, который работает, если T
считается классом T
Посмотрим, как это происходит.
-
В 15.1.1 указано, что объявление вашего friend
должно было бы назвать конструктор. Далее указывается, что имя класса не является типом-именем. Поэтому мы должны быть в порядке, пока T
не является типед-именем.
-
В 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...