
This article will look at a number of bugs in the implementation of the C ++ 11 standard that were present in Visual Studio 2012 and were fixed in Visual Studio 2013. Thus, we can now use C ++ 11 as the theory intends to use it. If you had to write “crutches” to bypass VS2012 bugs, you can now remove them.
Unfortunately, not all bugs were fixed, something migrated from VS2012 to VS2013, and new bugs appeared. Under the cut you will find a detailed analysis of the current state of affairs.
There is no more limit on the number of types in the variadic templates
Visual Studio 2013 supports variadic templates in full, things like std :: function or make_shared no longer have a limit on the number of arguments they can take. In Visual Studio 2012, this limit existed and was equal to 5 (five).
Fixed type inference bugs
auto lost alignment specifier
If you used auto to declare a variable of the type defined by the expression __declspec (align (...)), the alignment was handled incorrectly in VS2012, which led to incorrect data placement in memory and random crashes.
')
Decltype could not be used instead of type in all cases.
Despite the fact that the type defined via decltype should presumably be possible to use wherever conventional types can be used, VS2012 did not allow writing, for example, such code:
vector<int> a; decltype(a)::iterator iter = a.end();
declval caused compilation errors
Some code, correct according to the C ++ 11 standard, was not compiled in VS2012 due to incorrect interpretation of declval.
Suppose you want to declare the is_comparable template:
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 {};
This will not work in VS2012, since declval will not understand what T is.
Fixed bugs in smart pointers
Using the lambda function as a custom deleter broke the conversion to type bool
If you used lambda functions to determine what should happen when you delete a smart pointer, you could not use this pointer in the context of converting it to bool type:
auto stream_deleter = [](ofstream* os) { os->close(); }; unique_ptr<ofstream, decltype(stream_deleter)> p_log(&log_file, stream_deleter); if (!p_log)
Calling unique_ptr :: reset could lead to double deletion
The order of operations in the reset method did not match the order described by the standard. This could lead to double removal of the object. Example:
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();
Calling the reset method starts the SelfReferential destructor, which again triggers the reset. Double deletion occurs because the reset method resets the pointer to the object under control after it is deleted, and not before it.
shared_ptr, protected-destructor and nullptr
You could not create a shared_ptr for a class with a protected destructor by initializing it with nullptr:
class Interface { public: virtual void do_stuff() = 0; protected: ~Interface() {} }; class Implementation : public Interface { public: void do_stuff() override {
In Visual Studio 2013, this code is compiled, as expected by the standard.
Fixed bugs in type traits library
Incorrect is_function
is_function returns an incorrect result if the function passed to it contains too many arguments:
typedef void f(int, bool, int*, int[], int, int, int, int, int, int, int); is_function<f>::value;
Also, the result is erroneous for functions with a calling convention other than the default
Similarly, is_member_function_pointer fails to return the result correctly for methods with an explicitly defined convention.
is_member_pointer, on the contrary, does not work correctly with the __cdecl method:
typedef void (__cdecl A::*ccall_proc)(int, long, double); is_member_pointer<ccall_proc>::value;
The is_object was defined via is_function, so the above error with a large number of function arguments applies to it, leading to an incorrect definition of the object.
is_scalar did not recognize nullptr_t
is_scalar <nullptr_t> incorrectly returned false in VS2012 - the standard defines nullptr_t as a scalar type.
is_pod misunderstood void
is_pod mistakenly returned true in VS2012, although void is not a
POD typeis_constructible returned incorrect results for links
is_constructible behaved incorrectly with reference types, returning false for things like:
is_constructible<const string&, string>::value; is_constructible<const string&, string&&>::value;
Bugs in alignment_of and aligned_union
alignment_of in VS2012 generates generatee a false warning about an inaccessible destructor if you use it on a private destructor type.
Also, aligned_union worked incorrectly in VS2012:
typedef aligned_union<16, string>::type StorageType; sizeof(string);
The aligned_union must have a static alignment_value member containing alignment values ​​for the template arguments T1, ..., Tn. This, however, was not implemented in VS2012.
common_type erroneously returns void
Instead of a compilation error, as assumed by the standard, common_type returned void in VS2012:
common_type<int, string>::type;
common_type also returns void incorrectly for user-defined types, when the * is * conversion is possible:
struct A {}; struct AWrapper { AWrapper() {} AWrapper(const A&) {} }; common_type<A, AWrapper>::type;
result_of is not compiled in some cases.
If you decide to use the move-only argument with this template in VS2012, you will have trouble:
result_of<Copyable(MoveOnly&&)>::type;
Fixed bugs in STL containers and algorithms
minmax_element did not work
The standard defines two versions of this algorithm:
pair<Iter, Iter> minmax_element(Iter first, Iter last) pair<Iter, Iter> minmax_element(Iter first, Iter last, Compare comp)
They must return (first, last), where first indicates the smallest element, and last indicates the largest, or make_pair (first, first) if the range is empty. In VS2012, however, instead, make_pair (min_element (first, last), max_element (first, last)) was returned.
Containers mistakenly demanded that the types of elements required the presence of move-constructors
All container move constructors mistakenly required the element type to have a move constructor.
struct A { A() {} private: A(A&&); A(const A&); }; deque<A> source; deque<A> target(move(source));
Similarly, access statements for the map and unordered_map elements required the presence of move constructors:
map<string, A> m; A& elem = m["abc"];
Fixed errors related to parallelization and asynchrony
shared_future created from future
Another bug in VS2012 was in the implementation of the future and shared_future for reference types and void. This bug allowed the following code to be compiled (which is obviously wrong, since the future is a move-only type):
future<int&> f_ref; shared_future<int&> sf_ref(f_ref);
Memory leak in thread class
A bug that could lead to memory leaks at program completion. This happened because the thread created, but never destroyed the at_thread_exit_mutex object, as well as some internal data structures.
Useless promise wait functions
Due to a bug in Visual Studio 2012, the wait_for and wait_until functions of such future objects returned future_status :: deferred instead of future_status :: timeout or future_status :: ready, making these methods useless.
Invalid messages in future_error exceptions
Bug inconsistencies error code and its description, because of what, for example, receiving the exception "broken promise", the message will contain the text "future already retrieved". Only the error code was correct.
an atomic-template could not be defined for a type without a default constructor
You received an error message when you tried to use an atomic template for a type without a default constructor, although this is not true.
atomics worked slowly
In VS2012, atomic operations were sometimes overridden with integrity checks (made them where it was not necessary). Although it does not violate the standard, the code worked more slowly than it could. VS2013 has a completely new implementation of atomic operations, which runs much faster.
Fixed errors in generating random numbers
In debug mode, mersenne_twister_engine generated an erroneous assert if you tried to initialize it with zero.
The stream operator for subtract_with_carry_engine contained an error leading to undefined behavior.
independent_bits_engine and shuffle_order_engine did not initialize internal members in their displacement constructors, which sometimes resulted in endless loops.
Fixed bugs in the library of rational arithmetic
Several bugs were found in the library:
You could not write the following code in VS2012
ratio_add<ratio<1, 2>, ratio<1, 3>>::num; ratio_add<ratio<1, 2>, ratio<1, 3>>::den;
Instead, you were forced to refer to the numerator and denominator through their type:
ratio_add<ratio<1, 2>, ratio<1, 3>>::type::den; ratio_add<ratio<1, 2>, ratio<1, 3>>::type::num;
Another mistake was in the implementation of the comparison.
cout << "2/60 < -1/3: " << ratio_less<r2_60, r1_3>::value << endl;
Thus, in VS2012, you had to be sure that the denominator transferred to the template is always a positive number.
Another bug was that ratio_equal correctly defined inequality, but not always correctly defined equality:
ratio_equal<ratio<1, 4>, ratio<4, 16>>::value;
And here's another bug. When you have a ratio <N, D>, if D is zero or a number greater than intmax_t, your program is definitely invalid. Visual Studio 2012, however, did not detect such errors:
typedef ratio<1, 0> r_error; cout << r_error::den << endl;
typedef ratio<INTMAX_MIN, 1> r_error2; cout << r_error2::num << endl;
In the Visual Studio implementation, static_assert statements that should work in these situations are placed in the ratio constructor. But the constructor works only when creating a class object, and in the examples above this does not happen.
Similarly, some code is compiled, although it should not:
Other errors in Visual Studio 2013
Tuple_element does not check for overruns
tuple_element <I, array <T, N >> should verify that I <N and not compile if it is not. This did not happen until VS2013.
Invalid conversion to bool for std :: function
In some cases, the conversion could give an incorrect result in VS2012, since the object was not empty when, in theory, it should be:
Assignment for rvalues
Visual Studio 2012 does not prohibit assignments for rvalues, as defined by the standard:
struct Dummy { int _x; }; Dummy get_dummy() { Dummy d = { 10 }; return d; } get_dummy()._x = 20;
align () incorrectly updating out-parameters
The function correctly calculates the returned address, but incorrectly updates the last two parameters:
void* p = (void*)0x1;
time_put does not work with wchar_t
time_put does not generate output when wchar_t is initialized.
Conclusion
You can read the full list of changes in VS2013 (not only in C ++ 11) in
this post written by Stephan Lavavej.
In addition to implementing C ++ 11 features, Visual Studio 2013 fixed many bugs in existing compiler functionality and libraries, from incorrect compiler errors to memory leaks and poor performance. This is definitely a positive dynamic.
Unfortunately, VS2013 still contains a number of bugs inherited from VS2012 and adds some new ones. I’m writing a book about all this, it’s not finished yet, but you can read something
now .