Reasons for Using C++ 17

Using the logic that we should use tools that are as simple as possible and only adopt complexity where it is absolutely necessary, it feels like I could use a very early version of C++. What follows are the reasons why I have increased that to C++17. The idea will be to only use these feature and no more. Any new use will be add to this page.

#if !((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
    #error Requires C++17 or higher
#endif

I was going to picking C++ 11 as the baseline, but Visual Studio starts with 14 as a default so I initially decided to go with that. However as I learnt more C++ there were a few key items I wanted. These are listed below and they mean I will be using C++17 as a baseline.

In Code Checks

Note that Visual Studio does not honour the default __cplusplus macro. Which means the code itself can’t do a simple check if this standard level has been set. It ends up looking like this:

#if !((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
    #error Requires C++17 or higher
#endif

if constexpr

if constexpr” allows for compile time code removal. I really think this is key for a modern C++. Falling back to #defines does not work for templates. Using runtime checks is slow and also, in the example below, the compiler needs to be told to ignore any warnings from the other code path.

    if constexpr ( is_same_v<key_t, const char*> )
    {
        hash = (uint32_t)key[0];
    }
    else
    {
        hash = (uint32_t)key;
    }

Note that this is also possible in C++11 using structs for partial template specialisation, but that ends up as a lot of extra code just to get to a series of key inline functions that do the special part that is needed depending on the template parameters. It also make the code harder to write.

inline static member variable for a class

This is much more convienant than having to put the declaration in the class and then the static assignment at the end, outside of the class.

inline static const char* emptyConstString = "";

Class template argument deduction (CTAD)

This allows for a class like cString<allocator=heap> and then call it without the template brackets.

enum allocator::source { heap, stack };
// ...
template<int source = allocator::source::heap>
// ...
cString<> usingDefaultAlloc;
cString<allocator::source::stack> fasterAlloc;
// and replace it with
cString usingDefaultAlloc;
cString<allocator::source::stack> fasterAlloc;

However note that it is easier, and arguably cleaner, to create specific names for each case used. Use nameClass for the main class and then the default can be just name

using cString = cStringClass<allocator::source::heap>;
using cStringStack = cStringClass<allocator::source::stack>;

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.