Search

B.3 — Introduction to C++17

What is C++17?

In September of 2017, the ISO (International Organization for Standardization) approved a new version of C++, called C++17. C++17 contains a fair amount of new content

New improvements in C++17

For your interest, here’s a list of the major improvements that C++17 adds. Note that this list is not comprehensive, but rather intended to highlight some of the key improvements of interest.


C.1 -- The end?
Index
B.2 -- Introduction to C++14

33 comments to B.3 — Introduction to C++17

  • adsjhh

    Thanks Alex, this tutorial is very helpful to get the in depth view of all the feature C++ has to offer.
    I found this tutorial very helpful to get all most of my queries answered. Keep it up!!!

  • hellmet

    God I can now see why adding threads here will take time. It is a *huge* topic in its own right. It requires understanding the memory model, (understanding what it means in itself can take you to a host of other topics, computer theory and turing machines, computer architecture and organization (caches, memory stuff), hardware stuff, OS support for that hardware, compiler stuff), what threads intrinsically mean, how they interact etc.

    You will also learn how you any line of code you write is almost always f-ed up in many ways and how you might not notice it when running on your system but does happen on other systems. The u̷n̷ɨv̷e̴ʀֆe seems to conspire against you by making all of them b̷u̵g̷s go away just as you sit to ḏ̏ebu͇͌g̫̚ y͕̿ó̖u̿͜r̼̿ ͎͛ap̡̐p that seems to have been tamę̃d̨̜͕͛̈́̋ ̛̥̹̈͛ͅN̰̜͕͗̔͠Ò̰̫̓̀͢H͖͚̙̿̂͝A̡̘̦̽͘̚H̤͔̽͒̄͢Ȧ͈̹̦̂͝ by your debugger; but you can fe̲̍eĺ̼ ̳͠i͟͝t͘͜, the uneasy feeling that your application is up to no good with a l͊͢i͙͂͗͜f̳̑ȩ̘̓̀ ö͔f̙̠̀̚ ̤̗̓͌î̥ţ͂s͍̕ own͑͜ waiting to lay waste to any and all d͈̚â͍tḁ́ it ̳̀r̠̚ea̻̿d͔̽s,̜͑ ṡ̢t͙̾o͙̍res̬̾ a̻͞n͍͒d tṛ̂ḁ̈nsm͕͛its̀͜; even the logs suggest ì̠mp̣͘o̘̎ssi͐ͅbl͚̍è͎ con̦͛ţ͒ȓ͔ol flow jumping b/w threads to the point where it seems possessed and the rest of the̫͌ ̨͊lỏ̗g͎͑ ̜̽j̫͞ȕ̮s̥͙͡͠t̬͑ ̛̣͐͟ŕ͖̱̃ẽ̡̫̲͐͝a̫̍d ̼͐g͖͓͂̑ï̧̱̑b̰͋b̻̭̤̉̒̇͂͜ę͇̬̳̒̏͒͡r̞̞̣̿̾͠͞ͅi͕̲̰̺͆̾͑͊s͖͈̰̿̀́̎͜h̥̖̥̃̓̀͟͞. Though the debugger seems to hold the mã͍dnè̦s͊͢s i̮͛n̢͛ ̝͐p̌ͅl͇̓a͉̓c̦͌ê̮, you know it's still there, and it's slowly ļ̫̙̓̈͂e̯̟͉̋͐̎Aķ͂In̮͙͐͠G ȯ̗͕̥͒͡ų̣̖͒̂̇t̳̞̓͊, waiting until it drives you completely insane, to the point where you start to think shipping an application wit̩͌h ͔̀a͎͒ deb̙̕ū͕g̥̾g͚̉er̢̄ ̨̑a͇̽t͓̐tac̡̃h̃͢e̤̕ḓ͚̾̕ ̘̤͛̕i͇͓̋͡s ̟̤͐̉ṇ̈̕͢o͖͙͐̂t̥͙̂͑ ̧̤̊͑͢͝s̰̯͖͆̈̍c̯͎͆̌ű̥ḣ͉̚͟ ͉̮̹̔̐͐ḁ̼̞̊̎̋ ḇ̰̈́͠ad̨̛̹̅͘͢ ̱̝̐̾i̮̫̤̰̋̌͋͗d̛̖̙̫͋͌͘͜e̱̣̘̐̂̑̅͜ả̪̘͖̬̌̓͞.̦͇̟͗͑̈̕͜

    After growing a beard and a half, you finally start to make sense of this... but you know, the krakeň͓̆͢ i͍̽s̳͛ ơ̠̮̬̻̾͘̕ut there, it can sense your̡͌ ̛̟f̖͕̈́̉e͍̳̔̑à̼ṟ͘ and when you least expect it, it'll come to bite y̡̛̛̺̘͞o̧͔̝̪͌̆̎̓u̧͙͍̻̅̈́͗͠ ̯̽ḯ̬n͚͠ ͙͂y̻̪̮͛͂͠ǒ̜̮͠u̘̻͔̞̒̽̐͡ŕ̗̯̩̀̔ ̦̚ḃ̢̜̰͈̉̚͝e͈̅ḫ̻͇͕̏̿̉͘i͈̪̐͡n͍͍̪͈͋͛͊̒d̦̮̥̘̂͒͊͠s.

  • hellmet

    Hello again!

    So, I've programmed pretty much so far in a single thread. Now, I'd like to wet my toes in threaded programming. I have an application that logs some stuff by printing to std::cerr, but I want to pull that out into a separate library. I know there are excellent alternatives and I don't need to reinvent the wheel, but this is a great learning experience, and a brush up on my OS course.

    The idea is this. The main thread calls the log constructor to start the logging thread. At the end of main, just before return 0, it is Flush()ed and the destructor is called. (An explicit flush so that I don't throw exceptions in the destructor, hopefully). The main() starts some threads to process some stuff in parallel and joins them appropriately.

    So I created a log class with a static thread instance, so that a new thread is spawned on first construction of the class. Subsequent calls to the constructor do nothing, and prompt the user that multiple constructions have taken place (i.e, a runtime error). This class has a push method, to push a message onto a queue. This is protected by a mutex lock. I also have a while loop in the logging thread that flushes the queue if it's not empty and suspends the thread (condition variables). So far, I'm hoping the sync right.

    Each thread gets its own stack, so I can't catch the exception of one thread in another in the naïve way (the usual try-catch around std::thread t{} ... t.join()). Say in a thread, an exception was thrown. This exception propagates in that thread's stack. Just as I have a try..catch around main, I'll need to wrap these threads too, if I am to terminate it safely. Also, I am guaranteed to note all the messages generated by this thread just before it crashes, since .push() is synchronous, aren't I? I'm hoping the debugger can find exceptions in threaded scenarios too, but yeah, need to test it.

    I hope threads are added here soon! I sure am looking forward to it! It's a rabbit hole in its own right!

    PS: Are questions like this okay (long, drawn out)?

    • hellmet

      Never having programmed complex threaded programs, I'm having trouble wrapping my head around thread sync. This is what I'm trying to do, as an exercise. Alternatively print odd and even numbers from alternate threads in a strictly alternate manner.

      This is my code

      This is my thought process. Thread t1 starts and wins on owning the lock (assume), meaning t2 is waiting to acquire the lock (blocked at lock.lock()). t1 continues, prints, unlocks, notifies the only waiting thread and stops as it is blocked in wait. In the meantime, t2 now acquires the lock, prints, unlocks and notifies the only waiting thread, t1, and the process repeats.

      But I either seem to get a deadlock or an assertion failure from MSVC (unlock of unowned mutex).

      I moved the unlock to the last line in the for-loop, and it works! But this makes 0 sense to me. Say I'm in a thread and just finished printing (meaning I own the lock), shouldn't waiting cause a deadlock?

      • nascardriver

        I fail to see the question in your first comment, and I don't understand the question with the push. Can you try to shorten it?

        Please don't remove the includes, post examples that can be compiled without modifications.

        Both of your threads can be in line 14 at the same time (t1 unlocks and idles. t2 locks and unlocks, now both are unlocked). This leads to a deadlock.

        My threading is too rusty, I couldn't solve this seemingly simple problem yet. If you solve it before me, please let me know how.

        • hellmet

          The first comment, I meant to ask if my approach was correct. Forgot to put in there hahhe my bad.

          About the push. I'm trying to write a logging library that logs asynchronously. The IO is done in a separate thread, and different threads push log messages into a queue owned by the logging thread. The logging thread keeps dumping these to the disk or wherever it's configured to. My question is, if a thread crashes, what is a non-intrusive way to catch that exception and send it to the logging thread? I was thinking of some constexpr functions that compile away to nothing in release mode, but some things (such as logging the line number and file name) are pushing me towards using macros.

          Here's a pastebin of the second comment's code. https://pastebin.com/dHckgbiN

          If both are unlocked, one or the other should win over the lock in the next line right?

          I went back to my days in the OS course and did this problem in C, albeit with semaphores. There, it was more or less straightforward. C++ doesn't seem to have the concept of semaphores, and instead relies on condition_variable. This is from my reading so far.

          If I solve it, I'll let you know! But I'm struggling to find decent resources to shed some light on C++ threading. Too bad most books don't cover this topic. And damn if they aren't expensive AF here in my place.

          I'm really looking forward to seeing some tutorials here!

          • hellmet

            I think I'm getting it.

            This is the code that works, and then deadlocks at the end. I'll need to fix that last bit.

            This is how it goes. Say thread 0 starts and wins the lock, printing "Thread [0]: 0\n" to the terminal. Parallelly, thread2 is blocked waiting to get the lock. t1, after printing, notifies the other thread to wake up, but t2 doesn't wake up yet, as the lock is still held by t1. t1 goes to sleep by calling wait()*, releasing the lock after which t2 can actually acquire the lock, goes on to print, notifies the other, unlocks by calling wait. This dance of notifying one-another is the other and interleaved waits is the reason why we can print alternatively.

            I'm yet to figure out how to get the last iteration fixed. When I do, I'll post my findings here.

            * wait() releases a lock when a thread is put to sleep and reacquires the lock when woken up (atomically, duh, else there is no point of a 'mutex' :) ).

            • nascardriver

              > wait() releases a lock
              That's what I was missing. I kept running into the need of notifying and waiting atomically.

              > I was thinking of some constexpr functions that compile away to nothing in release mode
              Doesn't sound like it makes sense. There will be `std::source_location` in C++20, until then, you have to use macros for file names/line numbers.

              > if a thread crashes, what is a non-intrusive way to catch that exception and send it to the logging thread?
              Without a `try` in the crashing thread, I don't know if there's a standard way of catching another thread's exception.

              > Too bad most books don't cover this topic
              If you're looking for performance improvement, look into execution policies. There will be async/await in C++20. That should be asynchronous programming a lot easier.

              > I'm yet to figure out how to get the last iteration fixed
              The last loop cycle calls `wait`, but is never notified. Only call `wait` if you're not in the last cycle (i < 19).

              • hellmet

                > That's what I was missing.
                Yeah me too! I was reading this amazing book here (it's free!, free as in free beer) [http://pages.cs.wisc.edu/~remzi/OSTEP/]. Perhaps it would be nice to add this as a prerequisite (at the least, the concurrency part) if and when threads are added here. It really puts things into perspective.

                > There will be `std::source_location` in C++20
                Wow that's great! How does it work though? Does it require compiler support? From my cursory reading, it seems that this requires compiler support. For now, what if, I do this

                Is that too dirty :D

                > way of catching another thread's exception
                Yeah. Other than using exception_ptr, which seems convoluted and intrusive to use. I was thinking, what if I write a macro that starts the thread with some code around it (basically a try-catch) when in debug mode, but the macro just boils down to the normal code when in release mode. Or, I could just ask the user of my library to set one of my functions as the terminate handler. The latter seems appropriate.

                I also looked into std::terminate_handler and that seems to work, so I can try to print the thread that crashed in the log. But hmmm... how can I add more useful info. I could try reading the stack, but that becomes OS specific quickly (unless C++ has more tricks up its sleeve). But this has been really fun, but scary too. So much to learn, so little time.

                > look into execution policies
                Alright!

                > Only call `wait` if you're not in the last cycle
                Yeah that fixes it! I was thinking of some way to make it as general as possible, without this condition. Doesn't seem like it's going to be possible though. Also, I need to figure out a way to start the right thread first, always. Shouldn't be too hard now that the basics are falling into place. Thanks for the exchange!

                • nascardriver

                  > Does [std::source_location] require compiler support?
                  Yep. You'll have to wait for your standard library and compiler to support it. According to https://en.cppreference.com/w/cpp/compiler_support#C.2B.2B2a_library_features gcc libstdc++ is the only compiler/stdlib combo that supports it right now, and it's still experimental.

                  > Is that too dirty
                  It's dirty, but there's no way around it now. As a general recommendation, don't make your macros look like functions, they're not functions. If I assume your macro is a function because it looks like one, I could try to use it as a default argument, which won't do the same as a function.

                  > what if I write a macro
                  You like macros too much. You only need a macro to check for debug mode.
                  If you have to use ugly code, hide it behind good code.

                  That's the only preprocess magic you need, the rest is pure C++.

                  I wrapped it in a class, use a function if you like to, the idea is the same.
                  Please excuse the formatting, the code was too much for clang-format.

                  We extend `std::thread`, making `CSafeThread` a drop-in replacement.
                  If we're in debug mode, we wrap the function that was passed to the constructor in another function. That function calls the original function, but guarded by a `try`.
                  If we're not in debug mode, we simply forward the arguments to `std::thread`.

                  Read up on parameter packs, the code is easier than it looks.

                  Since you're in debug mode, why don't you just use a debugger and/or core dumps?

                  • hellmet

                    Yep, I'm looking at that page almost everyday! I can't wait to get my hands on the latest clang release! Boy most of the header stuff will just disappear! It'll make code more pythonic while maintaining performance of C++. I can't wait!

                    > You like macros too much. As a general recommendation ... don't!
                    Hahaha, perhaps... I was just playing around with it and yeah, it's misleading AF. I tripped myself with that haha.

                    > CSafeThread
                    How about member access then? I'll probably need to add a .get() method to return a reference to the thread so that normal std::thread methods can be called.

  • hellmet

    Hello Alex and Nascardriver! I have an unrelated doubt.

    In cmake, I have a directory structure like so

    Adding a cmake file for each of the subfolders and adding libraries for each (ex: in each of Device, Instance etc...) and linking them into a RenderLib would be one way. The other way is to just glob all of the files under Render to build a single RenderLib. Which is a better approach? I was thinking, with the first approach, compilation would be streamlined, but then again, cmake knows which files change and can compile only those units required. So I don't think there is any performance advantage in glob vs lib-per-module. What would you suggest?

    • nascardriver

      If you're writing the files in Render/ (ie. they're still changing), compile them (Either in a separate CMakeLists or in one for the entire project). If Render/ is a project on its own, compile it to a library link to it.
      As you said, the files will only be compiled once either way. If Render/ is a project on its own and you copy the files to a different project to use them, you might end up with several copies of the source files each with some minor differences you made along the way.

      • hellmet

        Yeah I'm writing all of them. It's not a project itself though, but I could make it one, so it's reusable in anything else I make... Hmm... For now, I'm not making it a project by itself. So I thought, I'll just glob all the files under Render and export a RenderLib. That's fine right?

  • Simon Allais

    There is also `std::variant` which is a C++17 template class representing type-safe union (useful when you simply want to use a "sum" type).

  • yoyos

    what will happen in C++20 and more? wil they continue to create new versions and keep people learning?

  • Soumya

    Hi Alex,

    I am a great fan of your website. But recently I saw in reddit that they discourage your site for learning C++ [https://www.reddit.com/r/learnprogramming/wiki/index#wiki_discouraged_resources].

    Actually I am worried and need your response to this. It would cleanse doubts in me and many others.

    Thanks.

    • Hi Soumya!

      This has been discussed on said subreddit several times, I couldn't find a statement of a wiki maintainer.

      Summing it up:
      Some people don't like that Alex uses "wrong" names to explain certain topics. Though, throwing technical words and descriptions at a beginner is discouraging. As long as there's a transfer to the proper names once the basics are set, I don't see a problem.
      Some people don't like the order of the chapters/lessons, others do like the current order.
      There were and still are inconsistencies across all lessons, because the lessons weren't all written on the same day obviously. Alex fixes these inconsistencies once they are pointed out. One of the complaints was about @std::endl and '\n' being mixed. This has been addressed by Alex and mostly, if not entirely, been fixed.
      You'll have to go through a lot of lessons before you get to the topics that separate C and C++. Again, this can be considered good or bad.

      My opinion (I haven't read the lessons (apart from 2 or 3), I just reply to comments and give suggestions to learners):
      Learncpp is dated. Alex appears to be busy irl, so I can't blame him for not yet having lessons about C++14, 17 and 20. But there is too few content about C++11, and the content that is covered falls short compared to the pre C++11 content. eg. 9 lessons about C-style arrays and only 3 about @std::vector and @std::array,  @std::string not being properly discussed until chapter 17!
      A lot of quizzes involve writing algorithms, but Alex rarely mentions the @std functions that could be used instead (eg. @std::sort, @std::accumulate, @std::end, etc.). The @std library is too important to be left out.
      The first couple of lessons teach bad practice, some of which stretches throughout the entire tutorials (eg. std::endl and not using uniform initialization). All lessons should use the recommended features. The discouraged features should be discussed briefly, stating the difference to their alternatives and why they shouldn't be used.
      Adding rules but not following them in the tutorials causes readers the forget about the rules and don't follow them either.

      Despite these problem, I think of learncpp as the best place to learn C++.
      learncpp is actively maintained, questions get answered and errors get fixed. There are quizzes every couple of lessons which help understanding the lessons. C++ is a difficult language and Alex does a great job explaining it.

      Resources
      - https://www.reddit.com/r/learnprogramming/comments/4wbeg5/is_learncppcom_still_considered_bad/
      - https://www.reddit.com/r/learnprogramming/comments/6z8en4/why_is_learncppcom_a_discouraged_resource/
      - https://www.reddit.com/r/learnprogramming/comments/3ncs7q/c_learncppcom_has_been_updated_is_it_a_reliable/

    • Rahul

      Things have been explained in a simple and easy way at this site. I have referred this website to many interns and young graduates in my team for a while, each one has come back that now they understand C++ better. I highly recommend this website to get foundation of C++ fundamentals right.

  • P-z

    2nd line:

      ISO (International Organization for Standardization)

    Shouldn't Organization and Standardization be flipped? Or IOS instead of ISO?

    • "ISO is not an acronym. The organization adopted ISO as its abbreviated name in reference to the Greek word isos (ίσος, meaning "equal"),[4] as its name in the three official languages would have different acronyms. "
      https://en.wikipedia.org/wiki/International_Organization_for_Standardization#Name_and_abbreviations

  • nascardriver

    "Nested namespaces can now be defined as namespace X:Y"
    is missing a colon
    "Nested namespaces can now be defined as namespace X::Y"

Leave a Comment

Put all code inside code tags: [code]your code here[/code]