Какой компилятор между Visual Studio 10 и GCC 4.5 является правильным в отношении перегрузки операторов и поиска, зависящего от аргументов? - PullRequest
2 голосов
/ 15 июня 2011

У меня есть следующий код:

class Foo;
class Bar;

class Bar {
public:
    Bar() {
    }

    Bar(Foo &foo) {
    }
};

class Foo {
public:
    Foo() {
    }
    Foo(Foo &foo) {
    }
    Foo(const Bar &bar) {
    }
};

Bar operator >> (const Bar &left, const Bar &right) { return Bar(); }

Foo a;
Foo b;
Foo c = a >> b;

В Visual Studio 10 вышеприведенный код прекрасно компилируется: компилятор распознает, что Bar может быть создан из Foo&, и поэтому вызывает соответствующий operator >>, который затем возвращает экземпляр Bar, и конструктор Foo(const Bar &) вызывается соответствующим образом.

Однако GCC 4.5 не компилирует приведенный выше код. Выводит следующую ошибку:

error: no matching function for call to 'Foo::Foo(Foo)'
note: candidates are: Foo::Foo(const Bar&)
note:                 Foo::Foo(Foo&)
note:                 Foo::Foo()

Почему вышеизложенное происходит и какой компилятор корректен в соответствии со стандартом языка?

EDIT:

Почему C ++ создает временный Foo объект в результате c = a >> b, поскольку Foo(const Bar &) существует?

Ответы [ 2 ]

4 голосов
/ 15 июня 2011

Это не имеет ничего общего с перегрузкой или поиском аргументов. Определив Foo(Foo&), вы отключили генерацию конструктора копирования по умолчанию Foo(const Foo&), который требуется для инициализации c из значения r. Добавьте ctor с подписью Foo (const Foo &), и ваш код будет работать нормально. Я не знаю, почему VS10 компилирует это, но я попытаюсь найти пункты, в которых указано, почему это не должно происходить.

Вот и мы: §12.8 (1) указывает, что не шаблонный конструктор для класса X является конструктором копирования, если его первый параметр имеет тип X &, const X &, volatile X & или const volatile X &, и нет других параметров, или все другие параметры имеют значения по умолчанию.

§12.8 (5) говорит, что если мы не определим конструктор копирования для X (в любой из вышеупомянутых форм), компилятор определит конструктор копирования в форме X (const X &).

Таким образом, определение Foo (Foo &) определяет a конструктор копирования, поэтому компилятор не может неявно определять Foo (const Foo &) больше.

2 голосов
/ 15 июня 2011

Это должно работать:

Foo c(a >> b);

Этот синтаксис инициализации:

Foo c = a >> b;

интерпретируется как

Foo c(Foo(a >> b));

копия-инициализация и требует доступного конструктора копирования, который принимает const Foo&.


Из раздела [dcl.init] стандарта (формулировка из C ++ 0x):

форма инициализации (с использованием скобок или =), как правило, незначительна, но имеет значение, когда инициализатор или инициализируемая сущность имеет тип класса;увидеть ниже.Если инициализируемый объект не имеет типа класса, список выражений в инициализаторе в скобках должен быть одним выражением.

Инициализация, которая происходит в форме

   T  x  =  a;

, а такжепри передаче аргумента функция return, выдача исключения (15.1), обработка исключения (15.3) и инициализация агрегатного члена (8.5.1) называются copy-initialization .[Примечание: инициализация копирования может вызвать движение (12.8).- примечание к концу]

Инициализация, которая происходит в формах

   T  x(a);
   T  x{a};

, а также в новых выражениях (5.3.4), выражениях static_cast (5.2.9), преобразованиях типов функциональных обозначений(5.2.3), а инициализаторы базы и члена (12.6.2) называются direct-initialization .

Синтаксис T x{a} является новым в C ++ 0x,все остальные правила не отличаются от C ++ 03.


Тогда применяются следующие правила (тот же раздел):

  • Если инициализацияпрямая инициализация, или если это инициализация копирования, когда cv-неквалифицированная версия исходного типа является тем же классом, или производным классом класса назначения, рассматриваются конструкторы.Применимые конструкторы перечислены (13.3.1.3), и лучший выбирается через разрешение перегрузки (13.3).Выбранный таким образом конструктор вызывается для инициализации объекта, с выражением инициализатора или списком выражений в качестве аргументов.Если конструктор не применяется или разрешение перегрузки является неоднозначным, инициализация некорректна.

  • В противном случае (т. Е. Для остальных случаев инициализации копирования) пользовательские последовательности преобразования, которыемогут преобразовываться из типа источника в тип назначения или (если используется функция преобразования) в его производный класс, перечисляются, как описано в 13.3.1.4, и лучший выбирается с помощью разрешения перегрузки (13.3).Если преобразование не может быть выполнено или является неоднозначным, инициализация неверна. Выбранная функция вызывается с выражением инициализатора в качестве аргумента;если функция является конструктором, вызов инициализирует временную версию cv-unqualified версии назначения.Временное является prvalue.Результат вызова (который является временным для случая конструктора) затем используется для прямой инициализации, согласно вышеприведенным правилам, объекта, являющегося местом назначения инициализации копирования. В некоторых случаях реализацияразрешается исключить копирование, присущее этой прямой инициализации, путем создания промежуточного результата непосредственно в инициализируемом объекте;см. 12.2, 12.8.

...