Comments 9
(・o・;)
> Его экземпляр создать невозможно, но компилятор это не волнует. Он действует на основании того, что ему сказали мы
--------------------------------
На основании того, что ему сказал Стандарт, а в комитете не только лишь Вы.
Почему такой код не считается ошибкой?
Derived(Derived const& d) : Base<T>(d) {}
Зачем C++ позволяет написать в шаблоне код, который заведомо не может скомпилироваться при использовании шаблона?
Сам догадался. Потому что может быть специализация шаблона Base
или Derived
где конструктор определён иначе:
template<>
struct Base<int>
{
Base() = default;
Base(Base const &) = default;
};
Потому что SFINAE.
Все такие ошибки (несовместимость типов, отчутсвующие методы и т п) происходят в момент использования шаблона, причём именно того самого метода, где происходит ошибка, а не в момент описания.
Компилятор пытается создать конструктор копированиятолько в момент его вызова. А если конструктор не вызывался бы нигде в коде, то ошибки компиляции не было бы.
Обычно это нужно для гибкости, чтобы можно было делать опциональные фичи в шаблоне требующие определённый тип и пропадающие с другими.
Здесь накладывается ещё возможность частичной специализации. В общем случае конструктора копирования может не быть (он компилируется в некорректный код), но есть частная специализация для int где конструктор реализован иначе и валиден.
Иногда думаю, что наследование это лажа какая-то. И больше 3х в глубину поддерживать - целая заморочка, и фиг перепишешь при куче интерфейсов, а вот эти хитро сделанные моменты как в статье, это однажды станут как thy thine thee в англ, старыми, и ненужными, неудобными и знать будет мало людей. Может однажды забудут реализовать в каком-нибудь потом компиляторе. Ну потому что фигня вот это сидеть и додумывать что же там копируется или нет, это не потому, что мы тупые, а потому что язык должен быть простым и понятным во всех смыслах.
Вот вы только представьте, что вы не филолог какой-то, который словам и предложениям пытается найти и дать определение, хорошие формулировки. Нет, представьте, что вы лопатами эти говны разбираете вагонами в сутки, хочется видеть понятный код, а не кал, который сидишь и анализируешь ещё, потому что как скажет кто-то такое, он конечно выпендрился, но ты видишь такое раз в 100 лет. Крякнешь улыбнёшься, и стираешь или дописываешь пусть и тупо, но понятно. Мне вот это разнообразие вариантов того, как можно выразиться, иногда доводит прям. Моя б воля может и ретурн бы убрал вообще, типа выход из функции только в конце. Ой, задело
Особенности кривизны C++. Инстанцирование шаблонных классов в нём не приводит к немедленному инстанцированию их методов и проверки корректности оных, методы компилируется, только если вызываются. Кода вызывается std::is_copy_constructible
, класс инстанцируется и вроде даже проверяется наличие конструктора копирования в нём, но его тело на корректность не проверяется, ибо не нужно.
Шаблоны тут вовсе не при чем. В C и C++ широко практикуется отделение объявления от реализации, и is_copy_constructible
далеко не всегда может проверить, что именно делает copy constructor. Вот например без всяких шаблонов:
$ cat module.h
struct Base
{
Base() = default;
Base(Base const &) = delete;
};
struct Derived: public Base
{
Derived() = default;
Derived(Derived const&);
};
$ cat module.cpp
#include "module.h"
Derived::Derived(Derived const&) : Base() {}
$ cat main.cpp
#include <type_traits>
#include "module.h"
static_assert(std::is_copy_constructible_v<Derived>);
int main() {}
Как прикажете is_copy_constructible
в main.cpp
проверять, что именно делает конструктор копии, реализация которого находится в module.cpp
? Что, если module.cpp
существует только в виде какого-нибудь module.so
?
Почему C++ считает мой класс копируемым, если его нельзя скопировать?