Finally someone with GOOD info, without the colossal attitude & ego! This kind of practical info is exactly why I watch these cpp videos. Thank you Ben!
@7:35 Maybe I'm misunderstanding what's going on here, but seems to me that the main reason not to define functions in header files is NOT that it slows the build process, but rather that it (almost always) BREAKS the build process, because it inevitably causes violations of the one definition rule down the line EVEN THOUGH you use #include guards. To see why such a violation would usually arise, imagine there is a library X that supplies function f. Furthermore, suppose libraries A and B use X. To do this, they would #include "x.h". You'll probably be able to compile A and B just fine (without violating ODR) thanks to #include guards, but if application K uses A and B, when you try to build K, the linker will see two "versions" (copies) of the definition of f floating around in the application. The fact that the two "versions" are exact copies of one another won't rescue you; the linker will still freak out on you, even though you could argue that it really doesn't need to, but not for some annoying rules of the language. The linker does not see that the original C++ definition of f exists only in one source code file (i.e. x.h), because that information does not get passed downstream from the preprocessor (and then further downstream by the compiler). All it knows is that two of the object files that are being fed in each contain a definition of f. You might ask "But why doesn't the linker just look at the code in those two definitions and notice that they are the same?". That's a sensible question, but it's possible that the two translations of f (one in A, the other in B) are different, even though they behave the same. There is no reliable way for the linker to detect that the two translations of f behave the same way, so we don't ask it to do that. Instead, we let the linker give up.
I assuming that Ben is answering this question under the prerequisite that we are considering the legal C++ domain. ODR-violations due to multiple definitions of non-inline functions is outside of the domain of legal C++. Indeed, a rule "always define your functions outside of source files" could help junior devs to coincidentally avoid ODR violations, but imho it's better to use education and tooling to teach and lint, respectively, the dos and don'ts of function definitions in headers (and beyond).
This is such a great talk. I was really struggling to understand how headers, compilation units and linkers work when I was starting out with C++, and spent so much time dealing with these errors as my programs were growing and started cross-referencing everything in the project. Especially when coming from "easier" languages where all this is completely hidden from the user. This should be one of the first videos people should watch! One thing I felt missing from the talk is any mention of precompiled headers and what pros and cons they have. And also with all the stuff Ben flew past at the end I feel like this really needs a part 2 :)
I must have misunderstood sth, cannot comprehend the second item on slide 37(37:50). How is it possible not violating *ODR* by just _"defining the entity in a single header that doesn't depend on including other headers"_ ? I test it in my MSVS with C++20 and get error. It is either possible using *inline* keyword denoting _the same entity_ for all translation units or *static* keyword for _different entities_ for each translation unit, afaik.
At 33:51, the function g was forward declared in b.o object file and defined in c.o. Is this acceptable? It doesn't have a extern keyword as well. If this is acceptable does the linker look for a particular function definition across all the available obj files before throwing undefined function error.
Yeah, I knew about the problem of huge compilation times arising from the same stuff generated from the same template in multiple object file and then linker removing the duplicates. Now I see that apart from longer compile times it is also dangerous in some cases, because the linker just picks any definition and does not have to care if they are actually the same. And if it does care it would increase the compilation times even further. So it is quite easy to introduce some UB by accident. Defining explicit specializations and using extern in the header file fore them solves both issues. This is an obvious solution for an experienced dev, but still it needs to be seen at least once to understand that that's the way! Thanks for reminding us the basics and pointing out to this very common example, where things can be made better just with a little effort. I wonder if that's the reason chromium takes forever to compile with regular(non-jumbo) build on Linux.
Linker scripts are a PITA. I usually have an idea of where RAM/Flash and abs addresses when i'm typing in the code source files. I don't want to write another obtuse file for linker obtuse statements. Some proprietary embedded compilers allows the "where" declarations in the source codes,so much better/cleaner.
For example at 18:10 I don't understand why int i; is a definition. To me it would be more logical that it's a declaration and that to be defined it should be int i = 12; for example. Is it because even though I did not assign a value to i I can still use it with its defauld garbage value ?
let me explain it in few words: Definition: memory will be allocated to it either by compiler(will make binary size bigger) or at runtime. Declaration: no memory is allocated for it yet. So for function/variable definitions: memory is allocated for code/variable at compile time and later in the binary.
Hi CppCon, the slides for this presentation are still not available to my knowledge (I just searched in the GitHub repo and could not find them), which is a shame as the remaining examples are probably very interesting too. Thanks in advance! (Great talk, by the way! Exactly what I needed right now, with a lot of information ❤)
Finally someone with GOOD info, without the colossal attitude & ego! This kind of practical info is exactly why I watch these cpp videos. Thank you Ben!
Glad it was helpful!
these basics are that kind of information that changes the way you think about your code process. Thanks!
@7:35 Maybe I'm misunderstanding what's going on here, but seems to me that the main reason not to define functions in header files is NOT that it slows the build process, but rather that it (almost always) BREAKS the build process, because it inevitably causes violations of the one definition rule down the line EVEN THOUGH you use #include guards.
To see why such a violation would usually arise, imagine there is a library X that supplies function f. Furthermore, suppose libraries A and B use X. To do this, they would #include "x.h". You'll probably be able to compile A and B just fine (without violating ODR) thanks to #include guards, but if application K uses A and B, when you try to build K, the linker will see two "versions" (copies) of the definition of f floating around in the application.
The fact that the two "versions" are exact copies of one another won't rescue you; the linker will still freak out on you, even though you could argue that it really doesn't need to, but not for some annoying rules of the language. The linker does not see that the original C++ definition of f exists only in one source code file (i.e. x.h), because that information does not get passed downstream from the preprocessor (and then further downstream by the compiler). All it knows is that two of the object files that are being fed in each contain a definition of f.
You might ask "But why doesn't the linker just look at the code in those two definitions and notice that they are the same?". That's a sensible question, but it's possible that the two translations of f (one in A, the other in B) are different, even though they behave the same. There is no reliable way for the linker to detect that the two translations of f behave the same way, so we don't ask it to do that. Instead, we let the linker give up.
I assuming that Ben is answering this question under the prerequisite that we are considering the legal C++ domain. ODR-violations due to multiple definitions of non-inline functions is outside of the domain of legal C++. Indeed, a rule "always define your functions outside of source files" could help junior devs to coincidentally avoid ODR violations, but imho it's better to use education and tooling to teach and lint, respectively, the dos and don'ts of function definitions in headers (and beyond).
Finally an understandable and comprehensive explanation! Thank you for this presentation.
This video was hugely helpful for me. Thanks a lot!
This is such a great talk. I was really struggling to understand how headers, compilation units and linkers work when I was starting out with C++, and spent so much time dealing with these errors as my programs were growing and started cross-referencing everything in the project. Especially when coming from "easier" languages where all this is completely hidden from the user. This should be one of the first videos people should watch!
One thing I felt missing from the talk is any mention of precompiled headers and what pros and cons they have. And also with all the stuff Ben flew past at the end I feel like this really needs a part 2 :)
Great presentation. I learn a lot from watching it.
Glad it was helpful!
I must have misunderstood sth, cannot comprehend the second item on slide 37(37:50). How is it possible not violating *ODR* by just _"defining the entity in a single header that doesn't depend on including other headers"_ ? I test it in my MSVS with C++20 and get error. It is either possible using *inline* keyword denoting _the same entity_ for all translation units or *static* keyword for _different entities_ for each translation unit, afaik.
At 33:51, the function g was forward declared in b.o object file and defined in c.o. Is this acceptable? It doesn't have a extern keyword as well.
If this is acceptable does the linker look for a particular function definition across all the available obj files before throwing undefined function error.
Good job. You had a lot of ground to cover there, and got through it quite admirably.
Extremally informative and great presentation. thank you for the efforts. Thank you Ben and CppCon.
Much appreciated!
watching this alters the time run
Yeah, I knew about the problem of huge compilation times arising from the same stuff generated from the same template in multiple object file and then linker removing the duplicates. Now I see that apart from longer compile times it is also dangerous in some cases, because the linker just picks any definition and does not have to care if they are actually the same. And if it does care it would increase the compilation times even further. So it is quite easy to introduce some UB by accident. Defining explicit specializations and using extern in the header file fore them solves both issues. This is an obvious solution for an experienced dev, but still it needs to be seen at least once to understand that that's the way! Thanks for reminding us the basics and pointing out to this very common example, where things can be made better just with a little effort. I wonder if that's the reason chromium takes forever to compile with regular(non-jumbo) build on Linux.
Linker scripts are a PITA. I usually have an idea of where RAM/Flash and abs addresses when i'm typing in the code source files. I don't want to write another obtuse file for linker obtuse statements. Some proprietary embedded compilers allows the "where" declarations in the source codes,so much better/cleaner.
Cool talk with good material to start with! Thanks!
For example at 18:10 I don't understand why
int i;
is a definition. To me it would be more logical that it's a declaration and that to be defined it should be
int i = 12;
for example. Is it because even though I did not assign a value to i I can still use it with its defauld garbage value ?
See 10:57
let me explain it in few words:
Definition: memory will be allocated to it either by compiler(will make binary size bigger) or at runtime.
Declaration: no memory is allocated for it yet.
So for function/variable definitions: memory is allocated for code/variable at compile time and later in the binary.
Hi CppCon, the slides for this presentation are still not available to my knowledge (I just searched in the GitHub repo and could not find them), which is a shame as the remaining examples are probably very interesting too. Thanks in advance!
(Great talk, by the way! Exactly what I needed right now, with a lot of information ❤)
yes very true, i need those slides too
Did you get the slides ? Can't find them
This guy is very articulate and good at teaching
Wow so helpful! Truly amazing
Glad it was helpful!
The slides of Ben Saks are not available on github. Is there any reason for that ?
Where can I find the slides for this talk?
Ben love your Back To Basics videos are superb !! I wish we were taught like this :)
Glad you like them!
This guy is great tutor and funny to watch.
Can you please link the presentation? Would like to see the slides that were missed in the end! Thanks!
Did you found the slides?
Really Helpful Talk.
When I reading the C++ book, these template stuff cost me a lot of time.
Good Refresher!
Nice one! :)
Imagine that household. They probably have classes for dinner 😂😂😂. Seriously though, great talk!
I wonder, why would anyone want to put certain function definitions into flash memory instead of RAM? Nice presentation btw.
Execute in place arrangement?
So the program (or parts of it) can survive a power-off/restart?
it was data, not code.
This guy is a "professional" trainer?
i have a not good english and he is dither a lot in presentation. this makes hard to understanding.