Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Более того, по сути выражение std::iterator_traits<It>::value_type — не более чем гениальный костыль, придуманный на заре STL для определения типа получающегося при разыменовании итератора, первый вариант будет работать только с типом для которого определена специализация iterator_traits<>, а вот для второго нужен лишь operator*().Немного неточно. Во-первых, при разыменовании итератора получается ссылочный тип (
T&), а value_type — это тип с отброшенной ссылкой (T). Во-вторых, у std::iterator_traits<It> есть реализация по умолчанию, в которой std::iterator_traits<It>::value_type равен It::value_type. А нужен этот вспомогательный класс затем, что у обычного указателя (T*) не может члена-типа данных value_type, поэтому стандартная библиотека предоставляет для указателей специализацию данного класса.Эта коллизия может произойти с любым кодом уже сейчас, по правилам С++11Этот пример вроде неплохо демонстрирует, почему оно так работает. Проблема инициализации через фигурные скобочки в том, что она делает два дела одновременно: предоставляет синтаксис для инициализации с помощью
std::vector(10, 20) создает обьект из 10 элементов, тогда как
std::vector{10, 20} создает обьект только из двух элементов.
std::initializer_list и работает как универсальная инициализация с контролем над сужением, которая может быть использована в любом месте, где синтаксисом языка допускается инициализация.В C++98 для создания такой конструкции MyAllocList пришлось бы обьявить шаблонной структурой, продекларировать тип внутри нее и использовать вот так:Надо учитывать, что такой подход дает большую гибкость, потому что шаблон черезMyAllocList<Widget>::type lw;
using нельзя специализировать. Если брать пример с iterator_traits, то мы сможем написать так:template<typename It>
using iterator_value_type = It::value_type;
но не сможем потом его специализировать, чтобы он работал и для обычных указателей.default — этот модификатор заставляет компилятор генерировать автоматические функции классаТут есть один неприятный момент: если компилятор не cможет эту функцию сгенерировать, то он не выдаст вам сообщение об ошибке, а просто втихушку не станет её генерировать. Скажем вот такой код отлично компилируется:
struct T {
std::unique_ptr<int> p; // unique_ptr не имеет конструктора копирования
T(const T&) = default; // поэтому компилятор не может сгенерировать этот конструктор для T
};
а ошибку вы получите, только когда попытаетесь данный конструктор использовать.Однако один нюанс все-таки существует — decltype возвращает ссылку для всех выражений отличных от просто имениЭто неправда, например:
std::string f();
decltype( f() ) s; // s будет иметь тип std::string
decltype работает так: если ей передать имя переменной или выражение доступа к члену данных (например, a->b.c) без скобочек, то тип будет совпадать с типом переменной или члена данных. Иначе это считается выражением, а любое выражение имеет свой нессылочный тип (назовем его T) и value category (не знаю, как это правильно перевести). В соответствии с value category добавляется ссылочность: для lvalue результат будет T&, для xvalue — T&&, для prvalue — T.#if !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS)
noncopyable( const noncopyable& ) = delete;
noncopyable& operator=( const noncopyable& ) = delete;
#else
private: // emphasize the following members are private
noncopyable( const noncopyable& );
noncopyable& operator=( const noncopyable& );
#endif
В этом случае точный тип derefUPLess известен только компилятору, его просто невозможно сохранить в переменной не используя auto. Конечно возможно написать так:Если лямбда ничего не захватывает (квадратные скобочки пустые), то её можно преобразовать в указатель на функцию:
bool (*derefUPLess)(const std::unique_ptr<Widget>&, const std::unique_ptr<Widget>&) =
[](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2)
{ return *p1 < *p2; };
Выход простой, если в классе Widget перемещающий оператор присвоения продекларирован как noexcept, обьекты будут перемещаться, если нет — копироваться.А если у Widget перемещающий конструктор не
noexcept, но копирующего конструктора нет, то все равно перемещаться. Эту функциональность реализует std::move_if_noexcept()unless copy constructor is not available, in which case move constructor is used either way and the strong exception guarantee may be waived
#include <atomic>
#include <vector>
#include <iostream>
#include <stdexcept>
struct A
{
A() : i(++cnt) {}
A(const A&) =delete;
A(A&& x) : i(x.i) {
std::cerr<<"A("<<i<<") is moved at "<<cnt<<"\n";
x.i=0;
if(++cnt > 7)
throw cnt;
}
int key() const { return i; }
static int cnt;
int i;
};
int A::cnt=0;
main()
{
std::vector<A> v;
std::cout<<v.capacity()<<std::endl;
for(int i=0; i < 5; ++i) {
try {
v.push_back(A{});
} catch(int x){ std::cerr<<"bang#"<<x; std::cerr<<std::endl; }
std::cout<<v.size()<<": ";
for(const auto& a : v) std::cerr<<a.key()<<" "; std::cerr<<std::endl;
}
return 0;
}
но вектор портится напрочьНу уж не напрочь, вектор останется старой длины, просто часть его элементов будет смувлена. C ним даже работать дальше можно (добавлять/удалять элементы и т.д.). Хотя стандарт ничего этого не гарантирует:
Otherwise, if an exception is thrown by the move constructor of a non-CopyInsertableT, the effects are unspecified.
интересно как авторы стандарта это мотивируют?А что делать то предлагаете, если копирующего конструктора нету, а перемещающий не
noexcept? Ошибку компиляции выдавать?Ошибку компиляции выдавать?
если вы его поймав, спокойно продолжаете с массивом работать, то это ваши проблемы
наконец-то стандарт признал что существующая в С++98 спецификация исключений неэффективна, признал ее использование нежелательным (deprecated)
noexcept, есть ещё и compile-time оператор c тем же именем, который принимает на вход выражение и возвращает false, если в составе этого выражения есть хоть один вызов функции без noexcept спецификатора, и true в противном случае (более подробно о правилах можно прочитать здесь):void f() noexcept;
void g();
cout << noexcept(f()); // true
cout << noexcept(g()); // false
cout << noexcept(true ? f() : g()); // also false
noexcept:void use_f() noexcept( noexcept(f()) ) // noexcept(true)
void use_g() noexcept( noexcept(g()) ) // noexcept(false)
Аннотация к «Effective Modern C++» Скотта Майерса