MSVC: объединение против класса / структуры с встроенными операторами друзей - PullRequest
1 голос
/ 12 июля 2009

Этот фрагмент кода компилируется и выполняется, как ожидается, в GCC 3.x и 4.x:

#include <stdio.h>

typedef union buggedUnion
{   
public:
            // 4 var init constructor
        inline buggedUnion(int _i) {
            i = _i;
        }

        friend inline const buggedUnion operator - (int A, const buggedUnion &B) {
            return buggedUnion(A - B.i);
        }

        friend inline const buggedUnion operator - (const buggedUnion &A, const buggedUnion &B) {
            return buggedUnion(A.i - B.i);
        }

        int i;

} buggedUnion;

int main()
{
    buggedUnion first(10);
    buggedUnion second(5);

    buggedUnion result = 10 - (first - second);

    printf("%d\n", result.i); // 0

    return 0;
}

MSVC, однако, не будет компилировать этот код, жалуясь:

main.cpp(60) : error C3767: '-': candidate function(s) not accessible
        could be the friend function at 'main.cpp(41)' : '-'  [may be found via argument-dependent lookup]
        or the friend function at       'main.cpp(45)' : '-'  [may be found via argument-dependent lookup]
main.cpp(60) : error C2676: binary '-' : 'buggedUnion' does not define this operator or a conversion to a type acceptable to the predefined operator

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

Некоторые примечания:

  • Это тестовый пример , чтобы показать проблему, оригинальный тип данных гораздо более сложный и тщательно спроектированный, хотя и не работает в MSVC (основной компилятор - GCC, хотя совместимость MSVC также желательна ).
  • Добавление 'public:' в начале декларации объединения не разрешает ее.
  • Добавление 'public:' перед тем, как каждый оператор не разрешит его
  • Преобразование тестового примера в структуру / класс исправляет , но это нежелательно (пожалуйста, не пламя, у меня есть причины. Большинство из них являются ограничениями языка C ++)
  • Метод оператора следует оставить в глобальная область действия (не является функцией-членом)

Оптимальное решение не будет основываться на перемещении объявления за пределы определения объединения по эстетическим причинам (более 24 различных комбинаций операторов и операндов), но будет выполнено, если другого решения не существует.

Ответы [ 3 ]

2 голосов
/ 12 июля 2009

Трудно сказать, какой из них правильный, так как неназванные struct s не разрешены стандартом (хотя они являются распространенным расширением), и, как таковая, программа плохо сформирована.

Редактировать : Это похоже на ошибку в msvc, так как следующий код, который является абсолютно допустимым, не компилируется.

union buggedUnion
{
    friend buggedUnion operator - (int A, const buggedUnion &B) {
        return B;
    }

    friend buggedUnion operator - (const buggedUnion &A, const buggedUnion &B) {
        return A;
    }

    int i;
};


int main()
{
    buggedUnion first = { 1 };
    buggedUnion second = { 1 };
    buggedUnion result = 3 - (first - second);
}

Вы можете обойти это, определив функции вне класса.

union buggedUnion
{
    int i;
};

buggedUnion operator - (int A, const buggedUnion &B) {
    return B;
}

buggedUnion operator - (const buggedUnion &A, const buggedUnion &B) {
    return A;
}

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

Обратите внимание, что я удалил ненужные typedef и inline s.

0 голосов
/ 12 июля 2009

Следующий код правильно компилируется в Visual C ++ 2008:

union buggedUnion
{
    int i;

    friend buggedUnion operator - (int A, const buggedUnion &B);
    friend buggedUnion operator - (const buggedUnion &A, const buggedUnion &B);
};

buggedUnion operator - (int A, const buggedUnion &B)
{
    return B;
}

buggedUnion operator - (const buggedUnion &A, const buggedUnion &B)
{
    return A;
}

Хотя в документации MSDN говорится, что написание определения функции-друга внутри класса фактически помещает функцию в область видимости файла, похоже, это не работает для объединений. Так как это работает для класса и структуры, я подозреваю, что это может быть ошибкой. Поскольку реализация, которую я дал выше, должна работать на GCC, я думаю, что это можно считать подходящим обходным путем, и ошибка, скорее всего, не будет исправлена ​​в MSVC.

0 голосов
/ 12 июля 2009

Вам необходимо объявить эти функции-друзья во внешней области, так как после того, как вы объявите их в классе, они больше не будут видны во внешней области. Так что либо переместите тело функции из класса, как сказал avakar, либо оставьте их в классе и добавьте следующую строку, чтобы заново ввести имя во вмещающую область:

extern const buggedUnion operator-(const buggedUnion& A, const buggedUnion&B);

int main()
{
    ...etc

Надеюсь, это поможет. Не уверен, что это ошибка, но мне кажется (?) Правильное поведение, теперь реализованное правильно, которое многие компиляторы интерпретируют по-разному. См .: --ffriend-инъекция в http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-Options.html.

...