Не может быть друга с автоматическим

#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 завершен.