Можете привести пример, когда они генерятся автоматически? Вот мой пример:
#include <iostream>
#define F {std::cout << __PRETTY_FUNCTION__ << std::endl;}
struct B
{
B() F
~B() F
B(const B&) F
B(B&&) F
B& operator=(const B&) F
B& operator=(B&&) F
};
struct A
{
B b;
};
int main()
{
A a1;
A a2(a1);
A a3(std::move(a2));
return 0;
}
С move-семантикой и && иногда случаются недоразумения. Первая проблема — это то, что теперь вместо 4-х методов класса по-умолчанию получается 4 + 2 дополнительных (опциональных), но если мы хотим воспользоваться move-семантикой, то должны их объявить. Проблема тут в том, что даже если все члены класса поддерживают move-семантику, такие методы не генерируются компилятором автоматически. Т.е. надо явно написать:
class A
{
public:
A(A&& a) = default;
void operator=(A&& a) = default;
private:
std::vector<int> myVec;
};
Тут спасает, конечно, новые навороты, типа =default, но душу это как-то не сильно греет.
Проблема номер 2 связана с тем, что для конкретных типов оптимизация && не применяется. Объясню на примере: предположим, что у нас есть класс A, который требует строку в качестве входного значения:
class A
{
public:
A(const std::string& name_) : name(name_) {}
private:
std::string name;
};
Если мы вызываем так: A(«my_name»), то тогда создается временный объект std::string, а затем происходит копирование и уничтожение. Казалось бы, тогда надо создавать так:
class A
{
public:
A(std::string&& name_) : name(std::move(name_)) {}
private:
std::string name;
};
Тогда вышеприведенный пример работает, однако такой — нет:
std::string name = "my_name";
A a(name);
Тогда мы приходим к тому, что надо 2 метода: один с const&, другой — c &&. Уже попахиваем мазохизмом. А если мы добавим в конструктор еще пару полей, типа std::string nick, std::string address, то тогда можно сразу бежать за валерьянкой. Спасает способ, приведенный в статье:
class A
{
public:
template<typename T>
A(T&& name_) : name(std::forward(name_)) {}
private:
std::string name;
};
Но это, как бы так помягче сказать, для особых ценителей искусства. В общем, пока в новом стандарте радуют только лямбды.
Безусловно, язык С справляется на 100% если речь идет про драйвера и низкоуровневые вещи. Я про другое. Про то, что на данный момент С является одним из самых непереносимых языков. Наверно, по непереносимости его переплюнет лишь ассемблер. Другие языки хорошо себя чувствуют на разных платформах. Просто С — он всеядный, т.е. доступен на огромном количестве платформ, и это иногда путают с переносимостью. Те же драйвера всегда пишутся под конкретную платформу и ось, что говорит о слабой переносимости. Каждый язык хорош для своих задач. Просто спецификация того, что char может быть 8-битным, а может быть и нет, говорит о том, что язык стараются адаптировать под различные платформы, а переносимость, т.е. портируемость этого кода на другие плафтормы, от этого лишь страдает.
Портируемость (переносимость, англ. portability) обычно относится к одной из двух вещей:
1. Портируемость — как возможность единожды откомпилировав код (обычно в некоторый промежуточный код, который затем интерпретируется или компилируется во время исполнения, «на лету», англ. Just-In-Time), затем запускать его на множестве платформ без каких-либо изменений.
2. Портируемость — как свойство программного обеспечения, описывающее, насколько легко это ПО может быть портировано. По мере развития операционных систем, языков и техники программирования, становится всё проще портировать программы между различными платформами. Одной из изначальных целей создания языка Си и стандартной библиотеки этого языка — была возможность простого портирования программ между несовместимыми аппаратными платформами. Дополнительные преимущества в плане портируемости могут иметь программы, удовлетворяющие специальным стандартам и правилам написания (см., например: Smart Package Manager).
Вот теперь мне хочется понять, какой из пунктов для C легко реализуется?
Ну вот пример: у меня есть файлы на диске file.1, file.2,… file.1000000. Мне надо пройтись по всем файлам и показать контент. Каждый файл, например, занимает 10 байт (хотя это не принципиально). Как это сделать переносимо по стандарту?
Как sizeof поможет решить, какой тип использовать? Например, мне нужно проитерировать миллион элементов. Как, используя sizeof и без дефайнов мне написать такой цикл «переносимо»?
C переносимый, поэтому в нём базовые целочисленные типы (char, short, int и др.) не имеют строго установленного размера, а зависят от платформы.
То, что C переносимый — это миф. Может раньше он и был таковым, но сейчас — увы. Вышепреведенная фраза очень похожа на следующую: «в целях заботы о пользователях мы поднимаем плату за обслуживание». Спасибо, не надо.
Интересно, много ли на свете программистов, которые хотели бы, чтобы они не знали размеры базовых типов? Наверно, это чтобы было быстро? Т.е. лучше быстро и глючно? Наверно поэтому практически любая библиотека включает в себя configure — чтобы переносилось, ведь это ж часть языка, т.к. язык все может, ведь он переносим, да? А обилие #define тоже наверно говорит о великолепной переносимости? А то, что стандартные библиотеки не могут кроссплатформенно открыть, например, pipe — это тоже говорит о великолепной переносимости. В общем, заявление о том, что для поддержки переносимости нет определенности — это, извините, к доктору. Если хочется переносимости, то тогда надо все четко специфицировать, как, например, в Java. И не надо лохматить бабушку.
Как правило, это разработчик, и его цель — покрытие кода юнит-тестами. А это значит, что скорее всего, юнит-тест будет весьма не полным
С тем же успехом можно написать, что цель разработчика — сделать бажный продукт, т.к. я не видел еще не разу продукта без багов. Тут многое зависит от менеджера и опытности программиста, а говорить «как правило» — несколько надуманно. Как правило, юнит тесты вообще не пишутся.
В общем, культура TDD — это не написание тестов для галочки, а написание всевозможных сценариев для проверки. И автор забыл упомянуть такой подход: нашли баг — напиши юнит тест, чтобы этот баг не всплыл обратно. После этого юнит тесты заиграют новыми красками.
Кстати, MSVC 11 CTP NOV12 выдает:
Вывод:
Если же реализовать так:
То:
Компилятор GCC 4.5.2
Тут спасает, конечно, новые навороты, типа =default, но душу это как-то не сильно греет.
Проблема номер 2 связана с тем, что для конкретных типов оптимизация && не применяется. Объясню на примере: предположим, что у нас есть класс A, который требует строку в качестве входного значения:
Если мы вызываем так: A(«my_name»), то тогда создается временный объект std::string, а затем происходит копирование и уничтожение. Казалось бы, тогда надо создавать так:
Тогда вышеприведенный пример работает, однако такой — нет:
Тогда мы приходим к тому, что надо 2 метода: один с const&, другой — c &&. Уже попахиваем мазохизмом. А если мы добавим в конструктор еще пару полей, типа std::string nick, std::string address, то тогда можно сразу бежать за валерьянкой. Спасает способ, приведенный в статье:
Но это, как бы так помягче сказать, для особых ценителей искусства. В общем, пока в новом стандарте радуют только лямбды.
Вот теперь мне хочется понять, какой из пунктов для C легко реализуется?
То, что C переносимый — это миф. Может раньше он и был таковым, но сейчас — увы. Вышепреведенная фраза очень похожа на следующую: «в целях заботы о пользователях мы поднимаем плату за обслуживание». Спасибо, не надо.
Интересно, много ли на свете программистов, которые хотели бы, чтобы они не знали размеры базовых типов? Наверно, это чтобы было быстро? Т.е. лучше быстро и глючно? Наверно поэтому практически любая библиотека включает в себя configure — чтобы переносилось, ведь это ж часть языка, т.к. язык все может, ведь он переносим, да? А обилие #define тоже наверно говорит о великолепной переносимости? А то, что стандартные библиотеки не могут кроссплатформенно открыть, например, pipe — это тоже говорит о великолепной переносимости. В общем, заявление о том, что для поддержки переносимости нет определенности — это, извините, к доктору. Если хочется переносимости, то тогда надо все четко специфицировать, как, например, в Java. И не надо лохматить бабушку.
С тем же успехом можно написать, что цель разработчика — сделать бажный продукт, т.к. я не видел еще не разу продукта без багов. Тут многое зависит от менеджера и опытности программиста, а говорить «как правило» — несколько надуманно. Как правило, юнит тесты вообще не пишутся.
В общем, культура TDD — это не написание тестов для галочки, а написание всевозможных сценариев для проверки. И автор забыл упомянуть такой подход: нашли баг — напиши юнит тест, чтобы этот баг не всплыл обратно. После этого юнит тесты заиграют новыми красками.