Почему это предупреждение от компилятора IBM XL C / C ++? - PullRequest
8 голосов
/ 24 октября 2009

Вот минимальный пример кода, который иллюстрирует проблему:

#include <iostream>

class Thing
{
   // Non-copyable
   Thing(const Thing&);
   Thing& operator=(const Thing&);

   int n_;

public:
   Thing(int n) : n_(n) {}

   int getValue() const { return n_;}
};

void show(const Thing& t)
{
   std::cout << t.getValue() << std::endl;
}

int main()
{
   show(3);
}

Это приводит к той же ошибке:

int main()
{
    show( Thing(3) );
}

Компилятор IBM XL C / C ++ 8.0 под AIX выдает следующие предупреждения:

"testWarning.cpp", line 24.9: 1540-0306 (W) The "private" copy constructor "Thing(const Thing &)" cannot be accessed.
"testWarning.cpp", line 24.9: 1540-0308 (I) The semantics specify that a temporary object must be constructed.
"testWarning.cpp", line 24.9: 1540-0309 (I) The temporary is not constructed, but the copy constructor must be accessible.

Я также попробовал g ++ 4.1.2 с "-Wall" и "-pedantic" и не получил диагностики.Почему здесь необходим доступ к конструктору копирования?Как я могу устранить предупреждение, кроме того, чтобы сделать объект копируемым (что находится вне моего контроля) или сделать явную копию для передачи (когда реальный объект дорог для копирования)?

Ответы [ 4 ]

9 голосов
/ 24 октября 2009

Правила для этого в §8.5.3 / 5 стандарта. Выявлены три основные ситуации. Первый включает инициализатор (в вашем случае «3»), являющийся либо lvalue, либо имеющим тип класса. Поскольку ни один из них не верен, то у вас есть третий случай: инициализация константной ссылки с помощью значения r, у которого нет типа класса. Этот случай описан в последнем пункте в 8.5.3 / 5:

В противном случае временный тип «cv1 T1» создается и инициализируется из выражения инициализатора с использованием правил инициализации нереферентной копии (8.5). Ссылка затем привязывается к временному. Если T1 связан со ссылкой на T2, cv1 должен иметь ту же квалификацию cv или большую квалификацию cv, чем cv2; в противном случае программа некорректна.

Редактировать: перечитывая, я думаю, что IBM правильно поняла. Ранее я думал о возможности скопировать временный, но это не источник проблемы. Чтобы создать временную инициализацию копии без ссылки, как указано в §8.5, ему требуется копия ctor. В частности, на данный момент это эквивалентно выражению типа:

Т х = а;

Это в основном эквивалентно:

Т х = Т (а);

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

T temp1(3);
T temp2(temp1); // requires copy ctor
show(temp2);    // show's reference parameter binds directly to temp2
3 голосов
/ 24 октября 2009

C ++ позволяет достаточно умным компиляторам избегать копирования временных объектов, одно нарушение правила как если бы разрешено стандартом. Я не знаком с компилятором IBM AIX C ++, но, похоже, он думает, что вызов show(3) требует копирования временной вещи. В этом случае C ++ требует, чтобы у вас был доступный конструктор копирования, даже если ваш компилятор достаточно умен, чтобы не использовать его.

Но почему show(3) требует копию в первую очередь? Это я не могу понять. Если повезет, Литб будет немного впереди.

1 голос
/ 26 октября 2009

У меня такое чувство, что ответ Джерри правильный, но есть еще несколько вопросов.

Что интересно, так это то, что существует ключевая проблема, охватывающая предыдущий абзац этого раздела ( 391 ). Эта проблема связана с тем, когда аргумент имеет тот же тип класса. В частности:

int main () {
  show ( Thing (3) );       // not allowed under current wording
                            // but allowed with Core Issue 391

  show ( 3 );               // Still illegal with 391
}

Изменение в основной проблеме 391 влияет только в том случае, если временное значение rvalue имеет тот же тип класса. Предыдущая формулировка имела:

Если выражение инициализатора является r-значением, у T2 - тип класса, а cv1 T1 совместимо со ссылками с cv2 T2,, ссылка связывается следующим образом:

[...]

Конструктор, который будет использоваться для создания копии, должен вызываться независимо от того, выполняется ли копия на самом деле.

Эта последняя строка - то, что делает show(Thing(3)) недопустимым в соответствии с текущим стандартом. Предлагаемая формулировка для этого раздела:

Если выражение инициализатора является rvalue, с T2 типом класса, и "cv1 T1" совместим со ссылками с "cv2 T2", ссылка привязывается к объекту, представленному rvalue (см. 3.10 [basic.lval ]) или подобъекту внутри этого объекта.

В этот момент я считал, что g ++, возможно, обновил свое поведение согласно 391 , но это изменение случайно включило случай инициализации копирования. Однако это не продемонстрировано версиями g ++, которые я тестировал:

class A{
public:
  A ();
  A (int);
private:
  A (A const &);
};

void foo (A const &);

void foo ()
{
  A a = 3 ;     // 3.2.3 (ERROR), 3.4.6(ERROR), 4.4.0(ERROR), Comeau(ERROR)

  foo ( 3 ) ;   // 3.2.3 (OK), 3.4.6(OK), 4.4.0(OK), Comeau(OK)
  foo ( A() );  // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
  foo ( A(3) ); // 3.2.3 (OK), 3.4.6(ERROR), 4.4.0(OK), Comeau(OK)
}

Я не могу найти ошибку в интерпретации Джерри для случая foo (3), однако у меня есть сомнения из-за несоответствия между различными поведениями компилятора.

0 голосов
/ 24 октября 2009

Что произойдет, если вы попытаетесь назвать временную вещь?

Thing temp(3);
show(temp);

...