#c 11 #friend
#c 11 #друг
Вопрос:
struct X
{
private:
int value_;
public:
X():value_(int())
{}
X(int value):value_(value)
{}
friend
//int operator (X lhs, X rhs);//THIS WILL WORK
//BUT THE ONE BELOW WON'T
auto operator (const Xamp; lhs, const Xamp; rhs)->decltype(lhs.value_ rhs.value_);
};
auto operator (const Xamp; lhs, const Xamp; rhs)->decltype(lhs.value_ rhs.value_)
{//If I change return type to "not auto" there is no problem with friendship
return lhs.value_ rhs.value_;
}
int main(int argc, char* argv[])
{
X a(5);
X b(6);
cout << a b;
return 0;
}
Не удается объявить дружбу с operator в сценарии, когда возвращаемый тип — auto. Есть решение для этого?
Комментарии:
1. На самом деле это не имеет ничего общего ни с friendship, ни с auto. Проблема в том, что в decltype компилятор считает, что X является неполным классом. Я не уверен, правильно это или неправильно по этому поводу.
2. Какой компилятор вы используете. Я могу заставить ваш пример с некоторыми незначительными изменениями работать. Но только в gcc. В этом случае msvc, похоже, содержит ошибки.
3. у меня есть как gcc 4.6, так и VS2010 sp1, и на обоих из них он не будет компилироваться в этой форме
4. @unapersson ну, очевидно, что компилятор ошибается, поскольку класс был определен.
Ответ №1:
Это работает с g 4.5.1:
class X {
...
friend auto operator ( const X amp; a, const X amp; b )->decltype( X::value_ X::value_ );
};
auto operator ( const X amp; a, const X amp; b )->decltype( X::value_ X::value_) {
return a.value_ b.value_;
}
Комментарии:
1. 1. Это работает. Единственное, что меня беспокоит, это то, что такое использование вообще не имеет особого смысла.
X
хорошо известен, никаких шаблонов не задействовано. Я бы предпочел использовать решение, отличное от C 0x, предоставив средства доступа только для чтения или сделав operator членом класса.2. Предполагая, что вы оставили
X::value_
не-static
, тогда тот факт, что это работает , кажется мне ошибкой…3. Но это не работает в Microsoft msvc. Я предполагаю, что OP обнаружил проблему с компилятором.
4. @Vlad Я думаю, что я выберу решение до c 0x, но мне кажется, что в VS определено НОЛЬ новых функций, которые не содержат ошибок. Я сам исправил ошибки в лямбдах, static_assert, decltype и rvalues.
5. @ Мы ничего не можем поделать: Вообще говоря, многие из этих ошибок в VC 2010 на самом деле были ошибками в стандартном проекте, который он моделировал (N3000). Поскольку VC 11 предположительно будет основан на N3290, многие из этих ошибок будут исправлены благодаря моделированию обновленного (читай: исправленного) стандарта.
Ответ №2:
Это похоже на ошибку компилятора, а не на что-то преднамеренное (какой компилятор? рассмотрите возможность подачи ошибки). Однако вы всегда можете использовать уродливую альтернативу, описанную в спецификации для auto
с функциями (конечно, отказ от этого является мотивацией для нового синтаксиса, но если это не сработает …), а именно:
decltype((*(X*)0).value_ (*(X*)0).value_)
У вас одинаковый X с обеих сторон, поэтому вы можете создать typedef в X, чтобы вам не приходилось вводить эту неприятность как в объявлении, так и в определении оператора.
(Редактировать) В случаях, когда у вас нет одинакового X с обеих сторон, вы все равно можете учесть объявление, чтобы оно не было слишком ужасным, подготовив шаблон (признак) с decltype и используя его для объявления оператора. Что-то вроде:
template <typename X, typename Y>
struct PlusType {
typedef decltype((*(X*)0).value_ (*(Y*)0).value_) Type;
}
template <typename X, typename Y>
PlusType<X, Y>::Type operator (X amp;x, Y amp;y);
Комментарии:
1. к сожалению, это всего лишь демонстрационный образец, исходный код немного сложнее, если вы хотите, я могу его обновить, но общая суть заключается в том, что X является шаблоном с параметром IntType, поэтому этот operator идеально добавил бы два «разных» типа объектов X<int> и X<long>, например. Вот почему decltype.
2. @ Мы ничего не можем сделать: я отредактировал ответ и добавил пример того, как обернуть уродливый decltype в признак шаблона для использования в более сложных случаях.
3. @Vlad: Потрудитесь объяснить, почему? Это взято непосредственно из предлагаемой спецификации.
4. Предлагаемая спецификация и то, что реализовано, могут быть разными вещами. На самом деле, это не будет работать для GCC. Может быть ошибка, может быть что-то еще — я не знаю. Решение, предоставленное @unapersson, работает.
Ответ №3:
На мой взгляд, в вашей ситуации использование auto не имеет особого смысла, вам, вероятно, было бы лучше сказать, что оператор возвращает int .
Проблема здесь в том, что для того, чтобы иметь возможность писать rhs.value_ rhs должен быть экземпляром полного типа. Тип X считается завершенным после закрытия } класса. Так что компилятор справедливо жалуется.
ПРАВКА3:
Поскольку мне кажется, что мое приведенное выше объяснение не было полным и могло привести к путанице.
struct X
{
//code ommited for brevity...
//at this line one has decltype(lhs.value_ rhs.value_) where
//lhs and rhs are of type X however the X type is not yet complete.
//The compiler has to however determine the type of the friend including
//the return type...
friend auto operator (const Xamp; lhs, const Xamp; rhs)-> decltype(lhs.value_ rhs.value_);
};
Редактировать:
Обсуждение VS2010, связанное с decltype, которое объясняет, почему это так, как в VS2010, а также объясняет, почему это стандартная компиляция, как это в VS2010, вы можете посмотреть здесь:
Моим первым вариантом было бы отказаться от auto. Однако, предполагая, что вы предоставили более простой пример для иллюстрации, и в вашем реальном случае auto действительно имело бы смысл, вы могли бы попробовать:
struct X
{
private:
int value_;
public:
X() : value_(int()) { }
X(int value):value_(value) { }
template <typename T>
auto operator (const Tamp; rhs) -> decltype(value_ rhs.value_)
{
return value_ rhs.value_;
}
};
int main(int argc, char* argv[])
{
X a(5);
X b(6);
std::cout << a b;
return 0;
}
ПРАВКА2:
Для полноты (и чтобы проиллюстрировать, что это не проблема с auto и friend, а скорее попытка использовать неполный тип, рассмотрим следующий код (который также достигает желаемого, но искаженным, не рекомендуемым способом :-)):
#include <iostream>
struct X
{
public:
X() : value_(int()) { }
X(int value):value_(value) { }
int value_;
};
struct A : public X
{
friend auto operator (const Xamp; lhs, const Xamp; rhs) -> decltype(lhs.value_ rhs.value_);
};
auto operator (const Xamp; lhs, const Xamp; rhs) -> decltype(lhs.value_ rhs.value_)
{
return lhs.value_ rhs.value_;
}
int main(int argc, char* argv[])
{
X a(5);
X b(6);
std::cout << a b;
return 0;
}
Код нормально компилируется в VS2010 и gcc-4.5.1
Комментарии:
1. @ds27680 спасибо за ваш ответ, ваши слова «Итак, компилятор справедливо жалуется» — я не согласен. Я не согласен по той причине, что доступ к типу X осуществляется извне (от friend), и именно поэтому я считаю, что это ошибка компилятора. Но спасибо за ваш ответ и за предоставленную ссылку, собираюсь прочитать его сейчас.
2. @ds27680 другая проблема с вашим подходом заключается в том, что у вас должен быть общедоступный доступ в X, и это неприемлемо.
3. @ Мы ничего не можем сделать хорошо, второй пример приведен только для того, чтобы доказать, что это не проблема, связанная с auto, и создание друга оператора не является решением. Я ранее предлагал вам решение, которое делает operator шаблонным членом класса. Это нормально работает в VS2010, но по какой-то причине оно не компилируется нормально в gcc 4.5.1 (на ideone) (хотя, на мой взгляд, так и должно быть) 🙂
4. @ Мы ничего не можем поделать с тем, что компилятор справедливо (или, по вашему мнению, нет) жалуется, проблема в объявлении друга из X, а не в определении оператора за пределами X. В объявлении friend делается попытка использовать X как завершенный тип, но в этом контексте X еще не является завершенным типом.
5. @ds внутри operator тип X завершен.