Когда вы захотите обновить версию Visual C++ компилятора (например, перейти с Visual Studio с 2013 на 2015), будет не лишним узнать, почему вы можете столкнуться с тем, что код, который прежде успешно компилировался и выполнялся, теперь будет вызывать ошибки компиляции и/или ошибки времени выполнения.
Эти проблемы могут быть вызваны многочисленными изменениями компилятора для соответствия стандарту С++, изменениями в сигнатурах функций или изменениями расположения объектов в памяти.
Для того, чтобы избежать ошибок времени выполнения (их, как известно, наиболее трудно найти), мы рекомендуем никогда не делать статическое связывание с двоичными файлами, полученными другой версией компилятора. Также при обновлении своей программы (EXE или DLL), убедитесь, что используемые библиотеки тоже собраны новой версией компилятора.
Если вы используете типы из CRT (C Runtime) или STL (Standard Template Library), не передавайте их между двоичными файлами (включая DLL), которые собраны разными версиями компилятора. Более подробно этот вопрос рассмотрен в Potential Errors Passing CRT Objects Across DLL Boundaries.
И в дальнейшем мы рекомендуем не писать код, который зависит от конкретного расположения объектов в памяти (если это не COM интерфейс или POD объект). Если у вас сейчас есть такой код, после обновления компилятора вам следует убедиться, что все работает как надо. Более подробно можно почитать здесь: Portability At ABI Boundaries (Modern C++).
Ниже в статье описываются изменения в компиляторе Visual C++ (который идет с Visual Studio 2015 Preview). В статье слова «новое поведение» и «сейчас» относятся именно к этой версии, а «старое поведение» и «раньше» — к Visual Studio 2013 (и более ранним версиям).
Краткое содержание:
— Изменения в компиляторе
— Изменения в C Runtime Library (CRT)
— Изменения в STL
— Изменения в MFC и ATL
Для того, чтобы сделать возможными новые оптимизации и различные отладочные проверки, реализация С++STL в Visual Studio не ставит целью сохранение обратной совместимости на уровне двоичных файлов, поэтому объектные файлы и статические библиотеки, полученные при использовании разных версий STL, несовместимы, т.е. не могут быть скомпонованы в один двоичный файл (EXE или DLL), а объекты типов из STL не могут быть переданы между двоичными файлами, скомпилированными разными версиями компилятора. Такое смешивание двоичных файлов вызовет ошибку компоновщика про несовместимость _MSC_VER (это макрос, который содержит мажорную версию компилятора — например, 1800 для Visual Studio 2008 или раньше).
Эти проблемы могут быть вызваны многочисленными изменениями компилятора для соответствия стандарту С++, изменениями в сигнатурах функций или изменениями расположения объектов в памяти.
Для того, чтобы избежать ошибок времени выполнения (их, как известно, наиболее трудно найти), мы рекомендуем никогда не делать статическое связывание с двоичными файлами, полученными другой версией компилятора. Также при обновлении своей программы (EXE или DLL), убедитесь, что используемые библиотеки тоже собраны новой версией компилятора.
Если вы используете типы из CRT (C Runtime) или STL (Standard Template Library), не передавайте их между двоичными файлами (включая DLL), которые собраны разными версиями компилятора. Более подробно этот вопрос рассмотрен в Potential Errors Passing CRT Objects Across DLL Boundaries.
И в дальнейшем мы рекомендуем не писать код, который зависит от конкретного расположения объектов в памяти (если это не COM интерфейс или POD объект). Если у вас сейчас есть такой код, после обновления компилятора вам следует убедиться, что все работает как надо. Более подробно можно почитать здесь: Portability At ABI Boundaries (Modern C++).
Ниже в статье описываются изменения в компиляторе Visual C++ (который идет с Visual Studio 2015 Preview). В статье слова «новое поведение» и «сейчас» относятся именно к этой версии, а «старое поведение» и «раньше» — к Visual Studio 2013 (и более ранним версиям).
Краткое содержание:
— Изменения в компиляторе
— Изменения в C Runtime Library (CRT)
— Изменения в STL
— Изменения в MFC и ATL
Изменения в компиляторе
- /Zc:forScope — Флаг компилятора
/Zc:forScope-
объявлен устаревшим (deprecated) и в будущем будет удален. Сейчас компилятор при использовании этого флага будет выкидывать предупреждение D9035.
Данный флаг используется для использования нестандартного расширения С++ — использование переменных, объявленных в описании циклаfor
за пределами этого цикла. Этот флаг необходим только если установлен другой флаг —/Za
(соответствовать стандарту), потому что без/Za
, использование переменных из описания цикла по умолчанию разрешено. Если вам не нужно заботиться о кроссплатформенности (например, не предполагается собирать код другими компиляторами), вы можете выключить флаг/Za
(установить свойству проекта «Disable Language Extensions» значение «No»). Если же вы заботитесь о кроссплатформенности и соответствия стандарту, значит такие участки кода нужно переписать, переместив объявление переменной выше цикла:
// zc_forScope.cpp // compile with: /Zc:forScope- /Za // C2065 expected int main() { // Раскомментить следующую строку для исправления ошибки // int i; for (int i =0; i < 1; i++) ; i = 20; // с флагом /Za переменная i здесь уже вне своего блока }
- /Zg
Флаг компилятора/Zg
(создание прототипов функций) больше недоступен для использования (раньше у него был атрибут deprecated)
- Теперь нельзя запустить юнит-тесты, использующие C++/CLI, из командной строки используя mstest.exe, взамен нужно использовать vtest.console.exe. Более подробно можно узнать об этом здесь: VSTest.Console.exe command-line options.
- Ключевое слово mutable
Теперь использованиеmutable
, в соответствии со стандартом, допускается только применимо к именам членов класса, и не может быть применено к ссылкам, или именам, объявленным какconst
илиstatic
. Пример:
struct S { mutable int &r; };
Раньше это компилировалось, теперь компилятор выдаст ошибку С2071. Для исправления, нужно просто убратьmutable
.
- char_16_t и char_32_t
Теперь нельзя использовать char_16_t и char_32_t в качестве псевдонимов пользовательских типов, потому что сейчас эти типы определяются как встроенные. Раньше было довольно распространенной практикой для авторов библиотек определять char_16_t и char_32_t в качестве псевдонимов для uint_16_t и uint_32_t, соответственно.
#include <cstdint> typedef uint16_t char16_t; //C2628 typedef uint32_t char32_t; //C2628 int main(int argc, char* argv[]) { uint16_t x = 1; uint32_t y = 2; char16_t a = x; char32_t b = y; return 0; }
Для исправления нужно убрать объявление псевдонимов и переименовать любые другие идентификаторы конфликтующие со вновь введенными.
- Нетиповые (non-type) параметры шаблонов
Код, включающий в себя нетиповые параметры шаблонов сейчас правильно проверяется на совместимость типов. Например, следующий код раньше компилировался без ошибок:
struct S1 { void f(int); void f(int, int); }; struct S2 { template <class C, void (C::*Function)(int) const> void f() {} }; void f() { S2 s2; s2.f<S1, &S1::f>(); }
Сейчас компилятор выдаст ошибку, так как тип параметра шаблона не соответствует типу переданного аргумента (тип параметра — указатель на константный метод, но f не является константной).
error C2893: Failed to specialize function template 'void S2::f(void)'
note: With the following template arguments:
note: 'C=S1'
note: 'Function=S1::f'
Для избавления от этой ошибки убедитесь, что тип аргумента шаблона полностью соотвествует типу параметра.
- __declspec(align)
Компилятор больше не принимает__declspec(align)
для функций. По правде говоря, он никогда и не принимал, но сейчас он будет выдавать ошибку С3323. Для избавления от нее просто уберите это выражение из объявления функции. Так как это и раньше не имело никакого эффекта, это не поменяет ничего в вашей программе.
- Обработка исключений
В обработке исключений произошло несколько изменений. Первое — объекты-исключения должны быть копируемыми и перемещаемыми. раньше подобный код компилировался, теперь будет ошибка:
struct S { public: S(); private: S(const S &); }; int main() { throw S(); // error }
Проблема здесь в том, что конструктор копирования объявлен приватным, таким образом, объект не может быть скопирован, что обычно требуется при обработке исключения. То же самое применимо в случае, когда конструктор объявленexplicit
.
struct S { S(); explicit S(const S &); }; int main() { throw S(); // error }
Для избавления от этой проблемы убедитесь, что конструктор для объекта исключения объявлен в публичной зоне иexplicit
.
При ловле исключения также требуется, чтобы объект был копируемым. Следующий код скомпилируется в ранних версиях Visual Studio, но сейчас будет ошибка:
struct B { public: B(); private: B(const B &); }; struct D : public B { }; int main() { try { } catch (D d) // error { } }
Эту ошибку можно исправить, приняв исключение по ссылке:
catch(D& d) { }
- Строки и макросы
Сейчас компилятор поддерживает определенные пользователем литералы (user defined literals — UDL). Как следствие этого, строки (точнее строковые литералы), за которыми без пробела поставлен макрос, интерпретируются как UDL, что может явиться причиной ошибки или неожиданного результата. Например, раньше это компилировалось без проблем:
#define _x "there" char* func() { return "hello"_x; } int main() { char * p = func(); return 0; }
Компилятор интрепретировал возвращаемое функцией func значение как строку «hello», и макрос, который раскрывался в «there», а затем соединял два этих литерала в один. Сейчас компилятор интерпретирует это как UDL, но так как не может найти определение _х среди известных ему UDL, он выдает ошибку:
error C3688: invalid literal suffix '_x'; literal operator or literal operator template 'operator ""_x' not found
note: Did you forget a space between the string literal and the prefix of the following string literal?
Для решения этой проблемы нужно поставить пробел между строкой и макросом.
- Строки, расположенные рядом
Так же, как и в предыдущем случае, из-за изменений в разборе строк, строящие рядом строковые литералы, которые не разделяет пробел, раньше интерпретировались как одна строка, а сейчас для корректной компиляции нужно добавить пробел:
char * str = "abc""def";
Просто добавьте пробел между строками:
char * str = "abc" "def";
- Размещающий new и delete
Для соответствия стандарту С++14 было изменено поведение оператораdelete
. Детали можно прочитать в C++ Sized Deallocation. Была добавлена форма глобального оператораdelete
, которая принимает размер объекта. Важность этого изменения в том, что если в вашем коде есть операторdelete
с такой же сигнатурой (соответствующая размещающему операторуnew
), вы получите ошибку компиляции (С2956, которая указывает на строку с использованием оператораnew
, так как именно в этом месте компилятор пытается определить подходящий операторdelete
).
Функцияvoid operator delete(void*, size_t)
была размещающим операторомdelete
, соответствующим функции размещающего оператораnew
—void* operator new(size_t, size_t)
из С++11. В новом стандарте С++14 этотdelete
стал обычной функцией деаллокации (то есть глобальным операторомdelete
). Стандарт требует следующего — если размещающийnew
ищет соответствующийdelete
и находит стандартную функцию, программа не скомпилируется.
Например, предположим, ваш код определяет размещающийnew
иdelete
:
void * operator new(std::size_t, std::size_t); void operator delete(void*, std::size_t) noexcept;
Проблема здесь в следующем: совпадение описания оператора размещающегоdelete
и описания нового глобального оператора. Для того, чтобы избежать такого совпадения, можно использовать тип, отличный от size_t, при описании размера в размещающих операторахnew
иdelete
. Здесь нужно принять во внимание, что size_t зависит от компилятора, в Visual C++ это псевдоним для unsigned int. Хорошим решением здесь будет использование перечисления:
enum class my_type : size_t {};
Затем, нужно изменить объявления размещающих операторовnew
иdelete
так, чтобы вторым параметром вместо size_t они принимали my_type. Также нужно будет подправить передачу аргумента в вызовnew
(например, с использованиемstatic_cast<my_type>
совершить преобразование из целочисленного значения в перечисление) и изменить определение размещающих операторов, совершив преобразования из перечисления в целочисленный тип. Кроме перечисления можно использовать, например, класс, у которого будет поле типа size_t.
В качестве альтернативного решения можно убрать размещающийnew
. Если ваш код использует размещающийnew
для реализации пула и передаваемый размер — это размер объекта для размещения или удаления, в этом случае новый операторdelete
может вполне подойти для удаления этого пула.
Если вы не хотите изменять поведение данных операторов в своем коде прямо сейчас, вы можете пока использовать флаг /Zc:sizedDealloc-, который вернет старое поведение. То есть не будет создано двух-аргументной функции удаления, и, таким образом, не будет никакого конфликта с определенной вами функцией удаления.
- Поля объединений (union data members)
Теперь ссылки не могут быть полем объединения (union). Раньше этот код компилировался, теперь — нет:
union U1 { const int i; }; union U2 { int &i; }; union U3 { struct {int &i;}; };
Теперь возникают следующие ошибки:
test.cpp(67): error C2625: 'U2::i': illegal union member; type 'int &' is reference type
test.cpp(70): error C2625: 'U3::i': illegal union member; type 'int &' is reference type
Для разрешения этой проблемы нужно изменить ссылку на указатель или просто обычное значение. Изменение типа на указатель потребует изменений в коде, который обращается к этому полю. Изменение типа на значение позволит менять значение в объединении, что повлияет на другие поля объединения. Также в этом случае может измениться размер объединения.
- Безымянные объединения (anonymous unions)
Безымянные объединения стали больше соответствовать стандарту. Раньше для безымянных объединений генерировался явный конструктор и деструктор. Сейчас они объявляются удаленными.
struct S { S(); }; union { struct { S s; }; } u; // C2280
В Visual Studio 2015 Preview генерируются следующие ошибки:
error C2280: '<unnamed-type-u>::<unnamed-type-u>(void)': attempting to reference a deleted function
note: compiler has generated '<unnamed-type-u>::<unnamed-type-u>' here
Для решения этой проблемы, нужно определить свой конструктор и/или деструктор
struct S { // Определяем конструктор по умолчанию, добавляя пустое тело S() {} }; union { struct { S s; }; } u;
- Объединения с анонимными структурами
Для того, чтобы обеспечить соответствие стандарту, было изменено поведение времени выполнения для членов анонимных структур в объединениях. Конструктор анонимных структур — членов объединений — больше не вызывается неявно при создании объединения. Также деструктор таких полей не вызывается неявно при выходе объединения из блока видимости. Пример:
#include <stdio.h> struct S { S() { printf("Creating S\n"); } ~S(){ printf("Destroying S\n"); } }; union U { struct { S s; }; U() {} ~U(){} }; void f() { U u; // Неявный вызов деструктора } int main() { f(); char s[1024]; printf("Press any key.\n"); gets_s(s); return 0; }
Раньше вызывался и конструктор и деструктор. Сейчас они не вызываются. Компилятор выдает предупреждения:
warning C4587: 'U::s': behavior change: constructor is no longer implicitly called
warning C4588: 'U::s': behavior change: destructor is no longer implicitly called
Для восстановления старого поведения нужно дать имя анонимной структуре. Поведение времени выполнения неанонимных структур остается независимой от версии компилятора.
#include <stdio.h> struct S { S() { printf("Creating S.\n"); } ~S() { printf("Destroying S\n"); } }; union U { struct { S s; } namedStruct; U() {} ~U() {} }; void f() { U u; } int main() { f(); char s[1024]; printf("Press any key.\n"); gets_s(s); return 0; }
В качестве альтернативного решения можно попытаться перенести код конструктора и деструктора структуры в новые методы и вызывать их из конструктора и деструктора объединения:
#include <stdio.h> struct S { void Create() { printf("Creating S.\n"); } void Destroy() { printf("Destroying S\n"); } }; union U { struct { S s; }; U() { s.Create(); } ~U() { s.Destroy(); } }; void f() { U u; } int main() { f(); char s[1024]; printf("Press any key.\n"); gets_s(s); return 0; }
- Разрешение шаблонов (template resolution)
В разрешение имен для шаблонов также были внесены изменения. В С++, при рассмотрении кандидатов при разрешении имен, вполне возможен случай, когда одно или более имен могут быть неверными конкретизациями (invalid instantiation) шаблона. Эти неверные реализации обычно не вызывают ошибок компиляции (т.к. используется принцип, известный как SFINAE).
Сейчас, если SFINAE требует от компилятора конкретизировать шаблон класса, любые ошибки во время выполнения этой конкретизации будут считаться ошибками компилятора. Раньше эти ошибки игнорировались. Например:
#include <type_traits> template<typename T> struct S { S() = default; S(const S&); S(S&&); template<typename U, typename = typename std::enable_if<std::is_base_of<T, U>::value>::type> S(S<U>&&); }; struct D; void f1() { S<D> s1; S<D> s2(s1); } struct B { }; struct D : public B { }; void f2() { S<D> s1; S<D> s2(s1); }
Новый компилятор выдаст следующее сообщение об ошибке:
type_traits(1110): error C2139: 'D': an undefined class is not allowed as an argument to compiler intrinsic type trait '__is_base_of'
..\t331.cpp(14): note: see declaration of 'D'
..\t331.cpp(10): note: see reference to class template instantiation 'std::is_base_of<T,U>' being compiled
with [ T=D, U=D ]
Причина ошибки в том, что в месте первого вызоваis_base_of
класс D еще не определен.
В этом случае исправление ошибки будет следующим: не нужно использовать такие проверки типов до определения классов, значит нужно перенести определение B и D d в начало файла. Если эти определения в заголовочных файлах, нужно проверить порядок включения, чтобы убедиться, что определения классов включаются раньше, чем используется проблемный шаблон.
- Конструкторы копирования
И в Visual Studio 2013 и в Visual Studio 2015 RC, компилятор создает конструктор копирования для класса, если в этом классе есть определенный пользователем конструктор перемещения, но нет определенного пользователем конструктора копирования. В Dev14 этот неявно генерируемый конструктор копирования помечается как "=delete".
Изменения в C Runtime Library (CRT)
Общие изменения
- Рефакторинг бинарных файлов
Библиотека CRT была разделена на два бинарных файла — на общая библиотеку CRT (Universal CRT — ucrtbase), которая содержит бОльшую часть стандартной функциональности, и библиотеку VC Runtime (vcruntime140), которая содержит зависимую от компилятора функциональность (например, обработка исключений и встроенные функции (intrinsics)). Если вы используете стандартные настройки проекта, тогда это изменение вас никак не затронет, так как компоновщик сам станет использовать эти новые библиотеки. Если же вы устанавливали свойство компоновщика проекта Ignore All Default Libraries в Yes, или вы использовали флаг линковщика /NODEFAULTLIB, тогда вам следует обновить свой список библиотек (свойство проекта Additional Dependencies), то есть включить в него новые библиотеки самому. А точнее — заменить старые CRT библиотеки (libcmt.lib, libcmtd.lib, msvcrt.lib, msvcrtd.lib) на новые эквиваленты. Для каждой из двух новых библиотек есть статическая (.lib) и динамическая (.dll) версии, а также релизная сборка (без суффикса) и отладочная (с суффиксом «d»). Более подробно здесь: CRT Library Features
<locale.h>
- localeconv
Функция localeconv, объявленная в locale.h теперь работает корректно в случаях, когда включена потоковая локаль (per-thread locale). Раньше эта функция возвращала Iconv глобальной, а не потоковой, локали.
Если вы используете потоковую локаль, вам следует проверить, как новое поведение localeconv повлияет на вашу программу.
<math.h>
- С++ перегрузка библиотечных функций
Раньше в<math.h>
перегружались некоторые, но не все, математические библиотечные функции. В<cmath>
была определена перегрузка остальных функций. Это вело к проблемам разрешения перегрузки, если включен только<math.h>
. Теперь все перегрузки перенесены из<math.h>
в<cmath>
.
- Приведение в соответствие стандарту чисел с плавающей точкой
В математической библиотеке было сделано много изменений для приближения к соответствию стандарту IEEE-754 и Приложению F стандарта C11, основное внимание было уделено особым случаям (NaN и бесконечности). Например, при передаче в функцию библиотеки NaN раньше часто возникала ошибка, сейчас — не возникает. Более подробно узнать об этом можно здесь: IEEE 754 Standard, Annex F of the C11 Standard.
Эти изменения не вызовут ошибок времени компиляции, однако программа может начать вести себя по другому — более соответственно стандартам.
- FLT_ROUNDS
Раньше этот макрос разворачивался в константное выражение, что было неправильно, так как способ округления числа мог измениться во время выполнения программы (например, с помощью fesetround). Сейчас FLT_ROUNDS это учитывает.
<new> and <new.h>
- new и delete
В предыдущей версии библиотеки зависимые от реализации операторы new и delete экспортировались из динамической библиотеки (например msvcr120.dll). Эти операторы сейчас всегда линкуются статически с вашими двоичными файлами, даже если используются динамические библиотеки.
Это не очень значительное изменение для нативного или смешанного кода (/clr), но если ваш код скомпилирован с флагом /clr:pure то после обновления код может перестать компилироваться. В этом случае нужно убедиться, что во всех нужных местах подключены заголовочные файлы<new>
или<new.h>
. Еще нужно учесть следующее: флаг /clr:pure объявлен устаревшим и будет удален в одном из следующих релизов.
<process.h>
- _beginthread и _beginthreadex
Функции _beginthread и _beginthreadex сейчас хранят ссылку на модуль, в котором определен поток. Это нужно для того, чтобы не выгружать модуль, до тех пор пока выполняется поток.
<stdarg.h>
- va_start и ссылочные типы
va_start теперь проводит проверку во время компиляции, не является ли переданный аргумент ссылочным типом. Если является, то, в соответствии со стандартом С++, это ошибка.
<stdio.h> и <conio.h>
- Все функции семейства printf и scanf теперь объявлены встроенными (inline)
Определения всех функций printf и scanf перенесено объявлены inline и перенесены в<stdio.h>, <conio.h>
и другие заголовочные файлы CRT. Это довольно значительное изменение, которое приводит к ошибкам линковщика (LNK2019, unresolved external symbol) для всех программ, которые объявляли эти функции, без включения нужных заголовочных файлов CRT. Таким образом, при появлении этой ошибки нужно включить данные файлы, либо, если пока нет возможности (или желания) править все исходники, в качестве временного решения можно просто добавить библиотеку для линковки: legacy_stdio_definitions.lib
Если ваш проект линкуется со статическими библиотеками, которые были скомпилированы старой версией компилятора, линковщик может выдать ошибку «unresolved external symbol». Эта ошибка может быть вызвана ссылками на внутренние функции: _iob, _iob_func, или функции вида _imp_*. В этом случае мы рекомендуем перекомпилировать все статические библиотеки новой версией компилятора.
Для определения всех неразрешенных символов можно воспользоваться dumpbin.exe, чтобы проверить, какие определения есть в двоичном файле, для этого нужно использовать следующую команду:
dumpbin.exe /LINKERMEMBER somelibrary.lib
- gets и _getws
Функции gets и _getws были удалены. gets была удалена из стандарта С11, потому что ее использование очень небезопасно. Функция _getws была просто расширением Microsoft для «широких» строк (wide strings). В качестве альтернативы можно использовать fgets, fgetws, gets_s, и _getws_s.
- _cgets and _cgetws
Функции _cgets и _cgetws также были удалены. В качестве альтернативы можно использовать _cgets_s и _cgetws_s
- Формат Infinity и NaN
Раньше бесконечность и NaN могли были представлены с помощью специфичных для Visual C++ охранных строк (sentinel string):
- Infinity: 1.#INF
- Quiet NaN: 1.#QNAN
- Signaling NaN: 1.#SNAN
- Indefinite NaN: 1.#IND
К этим значениям может быть добавлен знак.
С99 представил новые требования к тому как бесконечности и NaN должны быть форматированы, сейчас Visual C++ соответствует этим требованиям. Новые строки:
- Infinity: inf
- Quiet NaN: nan
- Signaling NaN: nan(snan)
- Indefinite NaN: nan(ind)
К этим значениям может быть добавлен знак.
- Форматирование чисел с плавающей точкой
Для увеличения точности стал применяться новый алгоритм форматирования. Эти изменения затронули функции семейства printf, scanf и функции, подобные strtod.
Пример:
printf("%.0f\n", pow(2.0, 80))
Старый вывод: 1208925819614629200000000
Новый вывод: 1208925819614629174706176
- Спецификаторы форматирования (format specifiers)
Было улучшена точность при использовании %a и %А. Был добавлен спецификатор %F (тоже самое %f, только строки для бесконечностей и NaN выводятся заглавными буквами).
Теперь спецификаторы стали проверяться на правильность. Например, раньше %hlhlhld интерпретировалась как %d, сейчас это будет ошибкой.
Подобная проверка добавилась в функциях семейства fopen. Раньше эти функции могли принять неправильную строку (например: r+b+). Сейчас это также будет ошибкой.
- Режим _O_U8TEXT
Функция _setmode сейчас корректно сообщает о режиме открытого потока, ели он открыт в режиме _O_U8TEXT. Раньше функция возвращала ошибочную информацию о режиме _O_WTEXT.
- snprintf и vsnprintf
В этом релизе были реализованы функции snprintf и vsnprintf. Раньше это были макросы.
- tmpnam
Раньше функции tmpnam и tmpnam_s генерировали имена файлов в корне диска (например \sd3c)
Сейчас эти функции генерируют более понятные имена во временной папке.
- Сокрытие реализации FILE
Раньше тип FILE был полностью описан в<stdio.h>
, что делало возможным для пользователя получить доступ к внутренностям этого типа. Сейчас библиотека stdio была изменена для сокрытия деталей реализации и внутреннее строение FILE стало недоступным для пользователя.
- _outp and _inp
Функции _outp, _outpw, _outpd, _inp, _inpw и _inpd были удалены.
<stdlib.h>, <malloc.h> и <sys/stat.h>
- strtof и wcstof
Раньше эти функции не могли установить переменной errno значение ERANGE, если аргумент не мог быть представлен в виде числа типа float. Сейчас это исправлено. Следует заметить, что данная ошибка была только в указанных двух функциях, а функции strtod, wcstod, strtold, и wcstold работали так ка нужно. Нужно учесть, что данное исправление может оказать большое влияние на выполнение программы.
- Функции выделения выровненной памяти (aligned allocation functions)
В предыдущей версии библиотеки функции выделения выровненной памяти (_alligned_malloc, _alligned_offset_malloc) могли принять запрос на выделение блока с выравниванием, равным 0, хотя значение выравнивания должно быть степенью двойки (чем 0, конечно же не является). Сейчас при запросе выравнивания 0 байт выдается ошибка. Это изменение может повлиять на программу во время выполнения.
- Функции управления «кучей» (heap functions)
Функции _heapadd, _heapset и _heapused были удалены. Эти функции больше не могут выполнять свою функцию, так как CRT начал использовать кучу Windows.
- smallheap
Флаг линковки smallheap был удален. Более подробно: Link Options.
<time.h>
- clock
В предыдущей версии функция clock была реализована с использованием Windows API GetSystemTimeAsFileTime, поэтому зависела от системного времени и, следовательно, не могла гарантировать монотонность. В новой реализации clock использует QueryPerformanceCounter и, благодаря этому, монотонность соблюдается.
- fstat и _utime
Раньше функции _stat, fstat и _utime неправильно обрабатывали летнее время. До Visual Studio 2013 все эти функции работали используя только летнее время.
В Visual Studio 2013 проблема в семействе функций _stat была исправлена, но та же самая ошибка осталась в семействах fstat и _utime. Эта ошибка была исправлена только сейчас, сейчас летнее время обрабатывается корректно и во всех функциях.
- asctime
В предыдущей версии asctime могла разбирать дату в формате с ведущими нулями, напримерFri Jun 06 08:00:00 2014
. Однако спецификация требует, чтобы вместо ведущих нулей использовались пробелы, т.е.Fri Jun 6 08:00:00 2014
. Функция была приведена в соответствие со спецификацией.
- strftime и wcsftime
Функции strftime и wcsftime теперь поддерживают следующие спецификаторы форматирования (format specifiers): %C, %D, %e, %F, %g, %G, %h, %n, %r, %R, %t, %T, %u и %V. Также парсятся и модификаторы E и O, но (пока) не используются.
Спецификатор %с определяет представление даты в подходящем для текущей локали виде. Для С локали по умолчанию это формат, аналогичный %a %b %e %T %Y (такую же форму генерирует asctime). В предыдущей версии %с определял неверный формат — MM/DD/YY HH:MM:SS. Это было исправлено.
- timespec и TIME_UTC
Заголовочный файл<time.h>
теперь определяет тип timespec и функцию timespec_get в соответствии со стандартом С11. Дополнительно определяется макрос TIME_UTC (для применения в функции timespec_get).
- CLOCKS_PER_SEC
Теперь макрос CLOCKS_PER_SEC разворачивается в целое число типа clock_t, как этого требует стандарт языка С.
Изменения в STL
Для того, чтобы сделать возможными новые оптимизации и различные отладочные проверки, реализация С++STL в Visual Studio не ставит целью сохранение обратной совместимости на уровне двоичных файлов, поэтому объектные файлы и статические библиотеки, полученные при использовании разных версий STL, несовместимы, т.е. не могут быть скомпонованы в один двоичный файл (EXE или DLL), а объекты типов из STL не могут быть переданы между двоичными файлами, скомпилированными разными версиями компилятора. Такое смешивание двоичных файлов вызовет ошибку компоновщика про несовместимость _MSC_VER (это макрос, который содержит мажорную версию компилятора — например, 1800 для Visual Studio 2008 или раньше).
- Заголовочные файлы STL
В структуру включения заголовочных файлов (далее — хедеров) STL были внесены некоторые изменения. Хедеры STL могут включать друг друга в любом (неопределенном) порядке, однако в общем случае вам следует включать в свой код все хедеры, которые требуется, не полагаясь на то, что какие то хедеры находятся внутри других. Это увеличивает портируемость между версиями библиотеки и платформами. В Visual Studio 2015 RC поменялось по меньшей мере два хедера, которые могут довольно сильно повлиять на ваш код. Первый —<string>
, которые теперь не содержит<iterator>
. Второй —<tuple>
, которые объявляет std::array, не включая<array>
. И если в вашем коде есть директива «using namespace std», вы объявляете свой тип «array» и включаете (например)<functional>
, то вы получите конфликт имен, так как<functional>
включает<tuple>
и std::array будет конфликтовать с вашим array.
- Аллокаторы и константность
Мы сейчас требуем от оператора сравнения для аллокаторов принимать константные аргументы для обеих сторон. Если ваш аллокатор объявляет оператор сравнения так:
bool operator==(const MyAlloc& other)
то вам нужно сделать его константным:
bool operator==(const MyAlloc& other) const
- Константные элементы контейнеров
Стандарт С++ всегда запрещал контейнеры с константными элементами (напримерvector<const T>
илиset<const T>
). Раньше Visual Studio принимала такие контейнеры, сейчас контейнер с такими элементами вызовет ошибку компиляции.
- std::allocator::deallocate
В прежних версиях Visual Studio std::allocator::deallocate(p, n) игнорировала аргумент «n». Стандарт С++ требует, чтобы «n» был равен значению, переданному в качестве первого аргумента при выделении памяти для «p» с помощью std::allocator::allocate. Но тем не менее, сейчас этот аргумент проверяется, и если не соответствует значению, переданному в std::allocator::allocate, тогда программа (скорее всего) аварийно завершится.
- hash_map и hash_set
Нестандартные заголовочные файлы hash_map и hash_set объявлены устаревшими и будут удалены в следующем релизе. Вместо них следует использовать unordered_map и unordered_set.
- Компараторы и operator()
Ассоциативные контейнеры (семейство «map») сейчас требуют чтобы компараторы имели константный метод для сравнения элементов. Следующий код в классе компаратора вызовет ошибку времени компиляции:
bool operator()(const X& a, const X& b)
метод нужно сделать константным:
bool operator()(const X& a, const X& b) const
- type traits
Старые имена для type traits, взятые из раннего черновика стандарта С++ были заменены на новые, введенные в С++11. В следующей таблице показаны старые и соответствующие им новые имена:
Старое имя Новое имя add_reference add_lvalue_reference has_default_constructor is_default_constructible has_copy_constructor is_copy_constructible has_move_constructor is_move_constructible has_nothrow_constructor is_nothrow_default_constructible has_nothrow_default_constructor is_nothrow_default_constructible has_nothrow_copy is_nothrow_copy_constructible has_nothrow_copy_constructor is_nothrow_copy_constructible has_nothrow_move_constructor is_nothrow_move_constructible has_nothrow_assign is_nothrow_copy_assignable has_nothrow_copy_assign is_nothrow_copy_assignable has_nothrow_move_assign is_nothrow_move_assignable has_trivial_constructor is_trivially_default_constructible has_trivial_default_constructor is_trivially_default_constructible has_trivial_copy is_trivially_copy_constructible has_trivial_move_constructor is_trivially_move_constructible has_trivial_assign is_trivially_copy_assignable has_trivial_move_assign is_trivially_move_assignable has_trivial_destructor is_trivially_destructible
- launch::any и launch::sync
Нестандартные перечисления launch::any и launch::sync были убраны. Вместо launch::any нужно использовать launch::async | launch::deferred, а вместо launch::sync — launch::deferred. Более подробно можно посмотреть здесь: launch Enumeration.
Изменения в MFC и ATL
- Microsoft Foundation Classes (MFC) больше не включаются в «типовую» установку Visual Studio из-за большого размера. Чтобы установить MFC, при установке нужно выбрать пользовательскую настройку и выбрать «Microsoft Foundation Classes». Если же вы уже установили Visual Studio, можно запустить процесс перестановки через панель управления.
Visual C++ Redistributable Package все еще включает эту библиотеку.
Комментарии (11)
crea7or
11.05.2015 00:33MFC отлично поработал, но пора и на покой.
maksqwe
11.05.2015 00:38Альтернатива? Wtl, который обновляется хорошо что если раз в несколько лет. Qt? Да, удобен, но если требуется все нативное?
.Net? Ну это же не наш метод интерфейсы накидывать в билдере да на С# писать.crea7or
11.05.2015 02:05+1Ну у меня до сих пор есть проекты живые на MFC, ещё в VC6 собираются :) А так для меня теперь WPF основной.
VioletGiraffe
11.05.2015 10:31+8Qt, ИМХО. WPF уж очень неудобен в написании. Приятный бонус — Qt кросс-платформенный.
SerJook
А внести изменения, так, чтобы ничего не сломать, они не умеют?
Door
Действительно, это же так просто.
VioletGiraffe
А что сломали? Я только вижу в заметке, что кучу всего починили. Когда нестандартный код компилится и как-то (непонятно как) работает, хотя и не должен — это ошибка.
Я разрабатываю кросс-платформенный проект (Мак, Линукс, Андроид, Винда — MSVC, GCC, Clang), работаю в основном под виндой, т. к. там самая удобная IDE. Знаете, как раздражает, когда я что-то написал и отладил, а потом оказывается, что в GСС и clang оно не компилится?
phprus
ABI в CRT и STL.
Точнее просто и в этот раз совместимого между версиями библиотек ABI нет и не предвидится.
maydjin
Угу, например вот такое перестаёт компиляться, обидно, да?