Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Баги реализации эталона С 11 в Visual Studio 2012, которые были поправлены в Visual Studio 2013

Anna | 24.06.2014 | нет комментариев

В этой статье будет рассмотрен ряд багов в реализации эталона С 11, которые имелись в Visual Studio 2012 и были поправлены в Visual Studio 2013. Таким образом мы сейчас можем применять С 11 так, как в теории предполагается его применять. Если для обхода багов VS2012 вам пришлось понаписывать «костылей» — сейчас их дозволено убрать.

К сожалению, не все баги были поправлены, кое-что мигрировало из VS2012 в VS2013, а также возникли новые баги. Под катом вы найдёте подробный разбор нынешнего состояния дел.

Огромнее нет лимита на число типов в variadic templates

Visual Studio 2013 поддерживает variadic templates в полном объёме, вещи как бы std::function либо make_shared огромнее не имеют лимита на число доводов, которые они могут принять. В Visual Studio 2012 данный лимит существовал и был равен 5 (пяти).

Поправлены баги, касающиеся итога типов

auto терял спецификатор выравнивания

Если вы применяли auto для объявления переменной типа, определяемого выражением __declspec(align(…)), align обрабатывался неверно в VS2012, что приводило к неправильному расположению данных в памяти и случайным крешам.

decltype дозволено было применять взамен типа не во всех случаях

Невзирая на то, что тип, определяемый через decltype ориентировочно должно быть допустимо применять всюду, где дозволено применять обыкновенные типы, VS2012 не дозволяла написать, к примеру, вот такой код:

vector<int> a; 
decltype(a)::iterator iter = a.end(); // оплошность в VS2012
declval приводил к ошибкам компиляции

Определенный правильный согласно эталону С 11 код не компилировался в VS2012 из-за неверной интерпретации declval.
Представим, вы ходите объявить образец is_comparable:

template<typename, typename = true_type>
struct is_comparable : public false_type 
{};

template<typenameT>
struct is_comparable<T,
    typename is_convertible<decltype(declval<T>() > declval<T>()), 
        bool>::type> : public true_type 
{};

Это не сработает в VS2012, от того что declval не поймёт, что такое T.

Поправлены баги в разумных указателях

Применение лямбда-функции в качестве custom deleter ломало реформирование к типу bool

Если вы применяли лямбда-функции для определение того, что должно случиться при удалении разумного указателя, вы не могли применять данный указатель в контексте реформирование его к типу bool:

auto stream_deleter = [](ofstream* os) { os->close(); };
unique_ptr<ofstream, decltype(stream_deleter)> p_log(&log_file, stream_deleter);

if (!p_log)     // compile error 
    cout << "Couldn't open file" << endl;
Вызов unique_ptr::reset мог привести к двойному удалению

Порядок операций в способе reset не соответствовал порядку, описываему эталоном. Это могло приводить к двойному удалению объекта. Пример:

class SelfReferential
{
    unique_ptr<SelfReferential>& _p_self;
public:
    SelfReferential(unique_ptr<SelfReferential>& p) : _p_self(p)
    {}
    ~SelfReferential() 
    {
        _p_self.reset();
    }
};

unique_ptr<SelfReferential> p;
p = unique_ptr<SelfReferential>(new SelfReferential(p));
p.reset();  // двойное удаление через ~SelfReferential

Вызов способа reset запускает деструктор SelfReferential, тот, что вновь вызвает reset. Двойное удаление происходит потому, что способ reset сбрасывает указатель на подконтрольный объект позже его удаления, а не до него.

shared_ptr, protected-деструктор и nullptr

Вы не могли сделать shared_ptr для класса с protected-деструктором, инициализировав его nullptr:

class Interface
{
public:
    virtual void do_stuff() = 0;

protected:
    ~Interface() {}
};

class Implementation : public Interface
{
public:
    void do_stuff() override
    {
        // ...
    }
};

shared_ptr<Interface> ptr1 = make_shared<Implementation>(); // OK
shared_ptr<Interface> ptr2 = nullptr;                       // оплошность

В Visual Studio 2013 данный код компилируется, как и предполагается эталоном.

Поправлены баги в библиотеке type traits

Неправильная работа is_function

is_function возвращает неверный итог, если переданная ей функция содержит слишком много доводов:

typedef void f(int, bool, int*, int[], int, int, int, int, int, int, int);
is_function<f>::value;     // false, правда должно быть true

Также итог ложен для функций с calling convention, хорошим от дефолтного

Подобно, is_member_function_pointer не удаётся правильно воротить итог для способов с очевидно заданным calling convention

is_member_pointer, напротив, не правильно работает с __cdecl-способом:

typedef void (__cdecl A::*ccall_proc)(int, long, double);
is_member_pointer<ccall_proc>::value;   // false, но должно быть true

is_object был определен через is_function, таким образом вышеуказанная оплошность с огромным числом доводом функции распространяется и на него, приводя к неверному определению объекта.

is_scalar не распознавал nullptr_t

is_scalar<nullptr_t> ложно возвращал false в VS2012 – эталон определяет nullptr_t как скалярный тип.

is_pod неверно понимал void

is_pod ложно возвращал true в VS2012, правда void не является POD-типом

is_constructible возвращал неверные итоги для ссылок

is_constructible вёл себя неверно со ссылочными типами, возвращая false для пророческой типа:

is_constructible<const string&, string>::value;
is_constructible<const string&, string&&>::value;
Баги в alignment_of и aligned_union

alignment_of в VS2012 генерирует generatee a ложные предупреждения о недостижимом деструкторе, если вы используете его на типе с приватным деструктором.
Помимо того, aligned_union работал неверно в VS2012:

typedef aligned_union<16, string>::type StorageType;
sizeof(string);       // 24
sizeof(StorageType);  // 16, но должно быть 24 либо огромнее

aligned_union должен иметь статический член alignment_value, содержащий значения выравнивания для шаблонных доводов T1, …, Tn. Это, впрочем, не было реализовано в VS2012.

common_type ложно возвращает void

Взамен ошибки компиляции, как это предполагается по эталону, common_type возвращал void в VS2012:

common_type<int, string>::type;     // void

common_type также возвращает void ложно для пользовательских типов, когда реформирование *is* допустимо:

struct A {};

struct AWrapper {
    AWrapper() {}
    AWrapper(const A&) {}
};

common_type<A, AWrapper>::type;     // void
result_of не компилируется в некоторых случаях

Если вы решите применять move-only довод с этим образцом в VS2012, у вас будут неприятности:

result_of<Copyable(MoveOnly&&)>::type;  // оплошность компиляции

Поправлены ошибки в STL-контейнерах и алгорифмах

minmax_element не работал

Эталоном определяется две версии этого алгорифма:

pair<Iter, Iter> minmax_element(Iter first, Iter last)
pair<Iter, Iter> minmax_element(Iter first, Iter last, Compare comp)

Они обязаны возвращать (first, last), где first указывает на минимальный элемент, а last — на крупнейший, либо make_pair(first, first) — если диапазон пуст. В VS2012, впрочем, взамен этого возвращалось make_pair(min_element(first, last), max_element(first, last)).

Контейнеры ложно требовали от типов элементов непременного наличия move-конструкторов

Все move-конструкторы контейнеров ложно требовали от типа элемента наличия move-конструктора.

struct A
{
    A() {}
private: 
    A(A&&);
    A(const A&);
};

deque<A> source;
deque<A> target(move(source));    // оплошность компиляции

Подобно, операторы доступа к элементам map и unordered_map требовали непременного наличия move-конструкторов:

map<string, A> m;
A& elem = m["abc"];  // оплошность компиляции

Поправлены ошибки, связанные с параллелизацией и асинхронностью

shared_future создаваемая из future

Ещё один баг в VS2012 был в реализации future и shared_future для ссылочных типов и void. Данный баг дозволял дальнейшему коду скомпилироваться (что очевидно ложно, от того что future — это move-only тип):

future<int&> f_ref; 
shared_future<int&> sf_ref(f_ref);    // компилируется, правда и не должно

future<void> f_void;
shared_future<void> sf_void(f_void);  // компилируется, правда и не должно
Утрата памяти в классе thread

Баг, тот, что мог приводить к утратам памяти при заключении программы. Это случалось потому, что поток создавал, но никогда не уничтожал объект at_thread_exit_mutex, а так же некоторые внутренние конструкции данных.

Непотребные функции wait в future, полученной от promise

Из-за бага в Visual Studio 2012, функции wait_for и wait_until таких future-объетов возвращали future_status::deferred взамен of future_status::timeout or future_status::ready, делая эти способы непотребными.

Неверные сообщения в future_error исключениях

Баг несоответствия кода ошибки и её изложения, из-за чего, скажем, получив исключение «broken promise», сообщение будет содержать текст «future already retrieved». Верным был только код ошибки.

atomic-образец не мог быть определен для типа без конструктора по-умолчанию

Вы получали сообщение об ошибки при попытке применения atomic-образца для типа без конструктора по-умолчания, правда это и не правильно.

atomics трудились медлительно

В VS2012 atomic-операции изредка перебарщивали с проверками целостности (делали их там, где это не непременно). Правда это и не нарушает эталона, код работал неторопливей, чем мог бы. VS2013 имеет абсолютно новую реализацию atomic-операций, которая работает гораздо стремительней.

Поправлены ошибки в генерации случайных чисел

В дебаг-режиме mersenne_twister_engine генерировал ложный assert, если вы пытались инициализировать его нулём.

Потоковый оператор для subtract_with_carry_engine содержал ошибку, приводящую к неопределенному поведению.

independent_bits_engine и shuffle_order_engine не инициализировали внутренние члены в своих конструкторах перемещения, что изредка приводило к безграничным циклам.

Поправлены ошибки в библиотеке разумной арифметики

В библиотеке было обнаружено несколько багов:

Вы не могли написать дальнейший код в VS2012

ratio_add<ratio<1, 2>, ratio<1, 3>>::num;
ratio_add<ratio<1, 2>, ratio<1, 3>>::den;

Взамен этого вы были обязаны обращаться к числитею и знаменателю через их тип:

ratio_add<ratio<1, 2>, ratio<1, 3>>::type::den;
ratio_add<ratio<1, 2>, ratio<1, 3>>::type::num;

Ещё одна оплошность была в реализации сопоставления.

cout << "2/60 < -1/3: " << ratio_less<r2_60, r1_3>::value << endl;  // false
cout << "2/60 < 1/-3: " << ratio_less<r2_60, ratio<1, -3>>::value << endl;    // true но должно быть false

Таким образом в VS2012 вы обязаны были быть уверенными, что знаменатель, переданный в образец — неизменно позитивное число.

Ещё один баг был в том, что ratio_equal верно определял неравенство, но не неизменно верно определял равенство:

ratio_equal<ratio<1, 4>, ratio<4, 16>>::value;   // false но должно быть true

И вот ещё одни баг. Когда у вас есть ratio<N, D>, если D — нуль либо число, превышающее intmax_t — ваша программ однозначно невалидна. Visual Studio 2012, впрочем, не определяла такие ошибки:

typedef ratio<1, 0> r_error;
cout << r_error::den << endl;   // не должно компилироваться, но компилируется
typedef ratio<INTMAX_MIN, 1> r_error2; 
cout << r_error2::num << endl;  // не должно компилироваться, но компилируется

В реализации Visual Studio, static_assert заявления, которые обязаны бы были срабатывать в этих обстановках, помещены в конструкторе ratio. Но конструктор срабатывает только при создании объекта класса, а в примерах выше этого не происходит.

Подобно, определенный код компилируется, правда и не должен:

// компилируется, правда и не должен - с предупреждением о переполнении
ratio_multiply<ratio<1, INTMAX_MAX>, ratio<1, 2>>::type;

Другие ошибки в Visual Studio 2013

В tuple_element не делается проверка выхода за границы

tuple_element<I, array<T, N>> должен проверят, что I < N и не компилироваться, если это не так. Этого не происходило до VS2013.

Неверное реформирование к bool для std::function

В некоторых случаях реформирование могло давать неверный итог в VS2012, от того что объект не был пуст, когда по идее должен быть им:

// JetPlane наследуется от Plane
function<bool(JetPlane*)> plane_ready_func = function<bool(Plane*)>();
if (plane_ready_func)   // должно быть false, но нет
{
    plane_ready_func(nullptr);   // вызывается и бросает bad_function_call
}
Присваивание для rvalues

Visual Studio 2012 не воспрещает присваивание для rvalues, как это определено эталоном:

struct Dummy 
{ 
    int _x; 
};
Dummy get_dummy() 
{ 
    Dummy d = { 10 }; 
    return d; 
}
get_dummy()._x = 20;     // компилируется, правда и не должно
align() неверно обновляет out-параметры

Функция правильно рассчитывает возвращаемый адресс, но неверно обновляет два последних параметра:

void* p = (void*)0x1;

// пытаемся выровнять 200 байт по границе в 32 байта 
// при всеобщем доступном объёме в 230 байт
size_t space = 230;
void* res = align(32, 200, (void*&)p, space);  
// res равен null от того что 31 байт нужен для выравнивания, 
// но в наличии только 30

space = 256;  // увеличиваем всеобщий объём до 256
res = align(32, 200, (void*&)p, space);
// res сейчас 0x20 (выровнян до границы в 32)
// p равен 0xE8 (200   32) но должен быть 0x20
// space равен 25 но должен біть  225
time_put не работает с wchar_t

time_put не генерирует итога когда инициализирован wchar_t.

Завершение

Полный список изменений в VS2013 (не только в C 11) вы можете прочесть вот в этом посте, написанном Stephan Lavavej.

В дополнение к реализации новых вероятностей С 11, в Visual Studio 2013 поправлено уйма баков в присутствующей функциональности компилятора и библиотек, от некорректных ошибок компилятора до утрат памяти и низкой продуктивности. Это однозначно позитивная динамика.

К сожалению, VS2013 всё ещё содержит некоторое число багов, доставшееся по наследству от VS2012 и добавляет некоторые новые. Обо всём этом я теперь пишу книгу, она ещё не завершена, но кое-что вы можете прочесть теснее теперь.

 

Источник: programmingmaster.ru

 

Оставить комментарий
БАЗА ЗНАНИЙ
СЛУЧАЙНАЯ СТАТЬЯ
СЛУЧАЙНЫЙ БЛОГ
СЛУЧАЙНЫЙ МОД
СЛУЧАЙНЫЙ СКИН
НОВЫЕ МОДЫ
НОВЫЕ СКИНЫ
НАКОПЛЕННЫЙ ОПЫТ
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB