Open In App

Removed Features of C++17

Last Updated : 02 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

C++17 enables writing simple, clearer, and more expressive code. Some of the features introduced in C++17 are:

  • Nested Namespaces
  • Variable declaration in if and switch
  • if constexpr statement
  • Structured bindings
  • Fold Expressions
  • Direct list initialization of enums

With the new version of C++17, many new functions are introduced but some features are removed or deprecated. These are listed below:

  • Removal of deprecated operator ++
  • Removal of registers
  • Removal of auto_ptr
  • Trigraphs
  • throw(typeid)
  • Allocator support in std::function
  • std::pointer_to_unary_function and std::pointer_to_binary_function
  • std::binder1st and std::binder2nd
  • std::bind1st and std::bind2nd
  • Other functions.

Let’s start discussing these functions in detail.

1. Removal of the deprecated operator ++: Postfix and prefix Increment (++) expressions are now not valid for bool operands since the prefix and postfix operator++ were overloaded for type bool, but in both cases, the return value for a bool argument is true. The bool type does not support the full set of arithmetic types. Since the launch of C++98, this change has been awaited. In the new version of C++17, it is no longer considered an arithmetic type and these operators have been deprecated.

Alternatives: The std::exchange can be used as an alternative for this, but only where the postfix operator has valid uses. The exchange function replaces the value of the object with a new value and returns the old value of the object.

2. Removal of registers: Long back, in C++11 register keyword was deprecated. The register keyword specifies or gives a hint to the compiler that the variable can be put in a register for fast access or these variables might be heavily used so that it can do optimization by storing them in a CPU register. But compilers do implicit optimizations and the hint was rarely used. Therefore, in the new version, the register keyword is removed, although the keyword is still reserved for future versions.

Syntax:

register string s = "Register on GfG"

 Alternatives: There is no alternative for register as the compiler does the same work automatically.

3. Removal of auto_ptr: auto_ptr was used to create a smart pointer to handle an object’s lifetime.  It is the owner of the object to which it refers. When an object gets destroyed, auto_ptr also gets destroyed automatically. This smart pointer quietly steals ownership of the managed object in its copy constructor and copy assignment from the right-hand argument. As a result, the copy is not the same as the original smart pointer object. Because of these copy semantics, auto_ptr does not work as CopyConstructible, and therefore it is deprecated. 

Alternatives: The auto_ptr can easily be replaced by, unique_ptr which is also a smart pointer with similar work but with improved security. It was introduced in C++11 as a direct replacement for auto_ptr as it provides new features (deletes) and support for arrays. Moreover, it allows only one owner of the referencing pointer. So, while using unique_ptr, there can only be at most one unique_ptr for one resource and when it is destroyed, the resource is automatically claimed. If there is an attempt to make a copy of unique_ptr, then it will cause a compile-time error.

Example:

unique_ptr<T> p1 (new T);
unique_ptr<T> p2 = p1;
// Error: can't copy unique_ptr

4. Trigraphs: The Trigraphs are a group of three characters. Basically, it is a special character sequence that is used as an alternative for some characters.  It is represented by two question marks.

Example:

??- produces ~ 
??= produces #
??/ produces \
??’ produces ^
??( produces [
??) produces ]
??! produces |
??< produces {
??> produces }

But they produce much confusion as they are parsed before comments and therefore removed in the latest version.

Alternatives: The C++17 does not provide any alternatives for Trigraph, as modern keyboards have all these features. Moreover, it produces a lot of bugs in the code.

5. throw(typeid): If any function is declared with type T listed in its exception specification, the function may throw exceptions to that type or a type derived from it. This is the non-throwing version of the dynamic exception specification that has been deprecated and is now removed. It has been replaced with noexcept which has a clearer meaning.

Syntax:

throw(typeid, typeid, ...)

Example:

void throwsInt(int x) throw(int) 
{
cout<<"throw function replaced with noexcept :)";
if (x == 0)
{
throw 1;
}
}

Alternatives: As mentioned above, throw can have a better alternative with noexcept. It specifies whether the functions can throw exceptions or not without specifying their type. But use it only when the invocation of the function cannot throw any error, otherwise, the program will terminate.

6. Allocator support in std::function: Several constructors allow to specify an allocator used for allocating internal memory. std::function also has constructors that take an allocator argument, but the semantics are unclear, and there are technical glitches with storing an allocator in a type-erased context and then recovering that allocator later for any allocations needed during copy assignment. Therefore, these constructor overloads were removed in C++17.

Alternatives: No such feature is present in C++, which replaces the allocator.

7. std::pointer_to_unary_function, std::pointer_to_binary_function: std::pointer_to_unary_function, std::pointer_to_binary_function function objects that act as wrappers around unary or binary functions. These functions include a constructor which constructs a new pointer_to_unary_function object with the supplied function and operator() function which calls the stored function.

Alternatives: These two functions  std::function and std::ref replace std::pointer_to_unary_function, std::pointer_to_binary_function.

8. std::binder1st and std::binder2nd: These are function objects that bind an argument to a binary function. The value of the parameter is passed to the object at the construction time and stored within the object. Whenever the function object is invoked through the operator() function, the stored value is passed as one of the arguments, the other argument is passed as an argument of operator(). The resulting function object is a unary function.

  • binder1st: Binds the first parameter to the value given at the time of the construction of the object.
  • binder2nd: Binds the second parameter to the value given at the time of the construction of the object.

Alternatives: Lambdas, std::bind are two features that can be alternatives for binder1st and binder2nd.

9. std::bind1st and std::bind2nd: These are helper functions that create instances of std::binder1st or std::binder2nd, which binds a given argument to a first or second parameter of a given binary function object. But these are of no use with the introduction of lambdas in C++11, therefore they were deprecated.

10. Other functions:

  • std::mem_fun_t,
  • std::mem_fun1_t
  • std::const_mem_fun_t
  • std::const_mem_fun1_t
  • std::mem_fun_ref_t
  • std::mem_fun1_ref_t
  • std::const_mem_fun_ref_t
  • std::const_mem_fun1_ref_t

These are function objects that wrap a pointer to a member function with no parameters or one parameter. The class instance whose member function to call is passed as a pointer to the operator() ie, the object whose member function to call is passed by the pointer to the call operator for the latter, is passed as a reference. They are deprecated because they are limited to member functions with either none or just one argument and different functions and function objects are required for handling pointers or references to the class instance.

Alternatives: The alternative to the above functions is std::mem_fn which can handle member functions with any number of variables and not only references or pointers to objects, but also smart pointers.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads