Что такое скрытый конструктор копирования срезов? - PullRequest
0 голосов
/ 03 ноября 2011

Этот вопрос из-за этого вопроса и комментариев .

Этот пример:

#include <iostream>

struct A {
    A(int value) : m_value(value) { }
    int m_value;
};

struct B : A {
    B(int value) : A (value) { }
};

int main()
{
    try {
        throw B(5);
    }
    catch(A) {
        std::cout << "A catch" << std::endl;
    }
    catch(B) {
        std::cout << "B catch" << std::endl;
    }
}

при компиляции с использованием g ++ 4.6.1 следующим образом:

g++ exception_slicing.cpp -ansi -pedantic -Wall -Wextra

производит следующий вывод:

exception_slicing.cpp: In function 'int main()':
exception_slicing.cpp:20:5: warning: exception of type 'B' will be caught [enabled by default]
exception_slicing.cpp:17:5: warning:    by earlier handler for 'A' [enabled by default]

и вывод A catch.

Я понимаю, что 1-й блок перехвата сработал из-за проблемы среза.

  1. Где говорится о конструкторе скрытого копирования в базовом классе?
  2. Где это говорит об этом поведении?

PS1 Пожалуйста, предоставьте ответы с цитатой из стандарта.
PS2 И я знаю, что исключения должны обрабатываться константной ссылкой.

Ответы [ 4 ]

3 голосов
/ 03 ноября 2011

В вашем случае, то же самое предупреждение появляется, даже если вы перехватываете по константной ссылке, где срезы не происходят. Ваша проблема в том, что, поскольку B является public подклассом A ==> каждый B is-a A, поэтому он может быть перехвачен первым обработчиком. Вероятно, вам следует заказывать обработчики от самых специфических до наименее специфичных.

Кроме того, вы печатаете "A" в обоих catch блоках.

1 голос
/ 03 ноября 2011

1 Где говорится о конструкторе скрытого копирования в базовом классе?

Он скрыт не так сильно, как неявный.

Использование n3290:

§ 12 Специальные функции-члены

1 / Конструктор по умолчанию (12.1), конструктор копирования и оператор назначения копирования (12.8), конструктор перемещения и оператор присваивания перемещения (12.8) и деструктор (12.4) являются специальными функциями-членами.[Примечание: Реализация будет неявно объявлять эти функции-члены для некоторых типов классов, когда программа явно не объявляет их.Реализация будет неявно определять их, если они используются odr (3.2).См. 12.1, 12.4 и 12.8.- конец примечания]

Итак, давайте следуем за указателем:

§ 12.8 Копирование и перемещение объектов класса

7 / Если определение класса явно не объявляет конструктор копирования, он объявляется неявно.[...]

8 / Неявно объявленный конструктор копирования для класса X будет иметь вид

X::X(const X&)

if
- каждый прямой или виртуальный базовый класс B из X имеет конструктор копирования, первый параметр которого имеет тип const B& или const volatile B& и
- для всех не статических членов данных X, принадлежащих к классутип M (или его массив), каждый такой тип класса имеет конструктор копирования, первый параметр которого имеет тип const M& или const volatile M&.

В противном случае неявно объявленный конструктор копирования будет иметь вид

X::X(X&)

И вот он у вас.В вашем случае есть неявно заданный для вас конструктор копирования A::A(A const&).


2 Где говорится об этом поведении?

Неудивительно, что вчасть, посвященная обработке исключений.

§ 15.3 Обработка исключения

3 / Обработчик соответствует объекту исключениятипа E, если

[...]

- обработчик имеет тип cv T или cv T&, а T является однозначным общедоступным базовым классом Eили

[...]

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

Стандарт продолжается:

4 / Обработчики для блока try пробуются в порядке появления.Это позволяет писать обработчики, которые никогда не могут быть выполнены, например, помещая обработчик для производного класса после обработчика для соответствующего базового класса.

Это действительно то, о чем действительно это предупреждение.

1 голос
/ 03 ноября 2011

Пример, который вы приводите, на самом деле не демонстрирует нарезку, он просто предупреждает вас, что, поскольку B - это A, улов (A) эффективно скрывает улов (B).

Чтобы увидеть эффект нарезки, вам нужно что-то сделать с буквой А в улове:

catch(A a) {
    // Will always print class A, even when B is thrown.
    std::cout << typeid(a).name() << std::endl;
}
0 голосов
/ 03 ноября 2011

Где это говорит о конструкторе скрытого копирования в базовом классе?

Стандарт ничего не говорит о "конструкторе скрытого копирования". В нем действительно говорится о неявно определенном конструкторе копирования.

Если вам нужна цитата:

Если определение класса явно не объявляет конструктор копирования, не существует объявленного пользователем конструктора перемещения, он объявляется неявно.

В C ++ 11 это может быть объявлено default или delete, в зависимости от содержимого рассматриваемого класса. Я не собираюсь копировать и вставлять полный список причин, по которым класс не может быть скопирован. Но достаточно сказать, что ваш класс получит default конструктор копирования.

Где это говорит об этом поведении?

О каком поведении? Поведение, которое вы видите, именно то, что вы ожидаете увидеть в следующем случае:

void foo(A) {}

void main() {
  foo(B());
}

Вы получаете нарезку, потому что параметр берется по значению. Для чего требуется копия.

Обработчики исключений не похожи на вызовы функций; C ++ не выберет наиболее близкое соответствие. Он выберет первое действительное совпадение. И поскольку B является A, то оно соответствует catch(A).

Опять же, если вам нужна какая-то цитата (хотя я не знаю почему; любая базовая книга по C ++, которая описывает обработку исключений, скажет вам следующее):

Обработчики для блока try пробуются в порядке появления. Это позволяет писать обработчики, которые никогда не могут быть выполнены, например, помещая обработчик для производного класса после обработчика для соответствующего базового класса.

Обратите внимание, что они даже приводят пример, который является именно вашим примером.

И я знаю, что исключения должны обрабатываться с помощью константной ссылки.

И это , почему вы берете исключения по ссылке.

...