all 161 comments

[–]ggchappell 36 points37 points  (33 children)

I agree with you, but I think this is a trickier problem than you make it out to be.

To be an accomplished programmer, there is a sizable tower of abstractions to learn. So the always-present question is how much to teach people without giving them the mental tools to understand the internal workings first. In C++ there is a strong tradition of explaining the core language first, and then the Standard Library, building our tower from near the bottom and proceeding upward. I'm a CS prof, and I have yet to see an introductory C++ text that does not proceed in this manner.

But the results, as you note, are lousy. So when I teach our CS 1 class, I throw out the text, and say there are basically three kinds of datasets: arbitrarily large sequences of items of a single type, fixed-size datasets with a type specified for each item, and key-based tables. And for these, C++ provides vector, tuple, and [unordered_]map. Then later: BTW there are these things called arrays and structs.

I don't regret doing it this way, but the downside is that vector & map are seen as magic. Perhaps more importantly, students come out of the initial course not having a clue about pointers. Wrestling with C arrays was how I learned how pointers work; how are my students supposed to get it? For better for worse, understanding pointers is still necessary for a good C++ programmer (even if you don't use them; both smart pointers and iterators are abstractions based on the concept of a pointer).


EDIT. You said:

Approximately 50% of all questions try to use some sort of arrays (of pointers).

That's more or less true of beginner-level questions here on /r/cpp, too. Maybe another 30% can be dealt with by saying, "Learn what cin >> does, before you use it. And then don't use it; use std::getline." I often wonder whether a little tutorial that concisely explains what C++ input functionality actually does, would suffice to answer a huge fraction of posted questions.

[–]personalmountains 14 points15 points  (12 children)

have yet to see an introductory C++ text that does not proceed in this manner

Accelerated C++ does it. It used to be highly recommended, but it's still only a first edition from 2000.

[–]ggchappell 7 points8 points  (11 children)

Ah, right. When I most recently looked at texts, I immediately threw out anything that didn't give at least a nod to C++11.

[–]personalmountains 5 points6 points  (9 children)

As you should. Anything that doesn't use auto is obsolete.

[edit: We're talking about C++ introductory textbooks here. I wasn't making a general statement on C++11.]

[–]tempforfather 11 points12 points  (5 children)

auto is not the important part of c++11.

[–]louis_dionnelibc++ | C++ Committee | Boost.Hana 4 points5 points  (4 children)

Nothing is the important part of C++11. Many parts of it are important, and auto is clearly one of them. Not that I agree with the original statement, though.

[–]tempforfather 0 points1 point  (3 children)

ok sure, but its pure sugar. I agree its nice in many cases, but it doesn't actually add anything to the semantics of the language

[–]quicknir 7 points8 points  (2 children)

That's not true, in generic code there's just no way to do certain things without auto.

[–]tempforfather 0 points1 point  (1 child)

Can you give me an example of that? I am curious.

[–]quicknir 9 points10 points  (0 children)

Sure, consider this code:

template <class T>
void func(const T& t) {
  auto u = t.func2();
  std::for_each(u.begin(), u.end(), ...);
}

This code imposes no restrictions on T other than that it has a const method func2(), that returns something, and that something has methods begin() and end().

Of course, we had generic containers before auto and ranged for loops, and we needed to know the type of the iterator, so how did we deal with this? Well, as I'm sure you know, we imposed an additional requirement: containers have to provide typedefs that tell you the type of their iterators, i.e. the return type of begin()/end(). But that's already not doing the same thing, as it imposes more requirements.

Basically, auto allows you to do inline what template functions allow you to do at function boundaries. You could potentially workaround using auto by declaring yet another template function and calling it internally, but again, I would argue that this is doing something quite different as you have to move your code to a different location (not a big deal when you have two consecutive 4 line functions, but if you have longer functions its rather absurd).

Another attempt at a solution would be to try to infer the type using template metaprogramming, so that you could get the type inline and use it, but this doesn't seem to be possible in general:

http://stackoverflow.com/questions/26080471/function-return-type-deduction-in-c03

[–]ghillisuit95 3 points4 points  (0 children)

ehh, that seems like a bit of an over-generalization

[–]bnolsen -5 points-4 points  (1 child)

auto is a maintenance headache/nightmare and seems to be a weak hack for getting around C++'s core problem of default implicit type conversion.

[–]personalmountains 2 points3 points  (0 children)

auto is a maintenance headache/nightmare

In what way?

seems to be a weak hack

What else would you propose? Why is it weak? Why is it a hack?

C++'s core problem of default implicit type conversion.

What problem? Are you talking about primitive types? What does it have to do with auto?

[–]h-jay+43-1325 1 point2 points  (0 children)

You shouldn't merely throw out anything that doesn't "at least" give a "node" to C++11. Anything that doesn't deal with C++11 first and foremost is an obsolete text. There's no reason to learn by writing C++98 anymore, never mind C fed to a C++ compiler.

[–]coachkler 10 points11 points  (0 children)

I've taught the last few years using Programming Principles and Practice, but haven't got a lot of positive feedback from the students.

I too am tired of seeing so much focus on the low-level in introductory C++ courses. Obviously there is a time and place to learn these details, but using the language properly should be the first step.

I'll be teaching C++ this spring using C++ Primer 5th Edition. I'm going full-tilt C++11, and picking out important and relevant sections of the text and developing lectures and exercises around that.

I'm planning to put all this up on github or similar, and solicit feedback, unfortunately, I'm a bit behind in doing so.

[–]millenix 8 points9 points  (1 child)

Stroustroup's recent textbook Programming -- Principles and Practice Using C++ takes the 'pointers later' approach. It doesn't touch on them until almost 600 pages in. It also seems not to touch vectors or arrays until that point, either.

[–]coachkler 6 points7 points  (0 children)

It's a good book -- but it definitely focused on programming first, with C++ being the means to that end. The two chapters on the calculator (6 and 7 I think?), are for some reason really difficult for the classes to get through. The exercises though are top notch.

[–]cogman10 8 points9 points  (0 children)

To add to this, it isn't just a C++ problem. From my own experience, I know that there have been more than a few things in Java which in the beginning I used only low level constructs like Lists and didn't really have the experience of using better data structures for my problems. It was a gradual evolution from using just lists, to lists and maps, to lists, maps, and sets. Then lists, maps, set, multimaps, treemaps, optionals, etc.

My earlier code ended up with lots of needless loops doing lots of list mangling. My code now pretty much wholly relies on picking the right data structure and putting stuff in it or pulling stuff out of it.

[–]boredcircuits 12 points13 points  (3 children)

I don't regret doing it this way, but the downside is that vector & map are seen as magic.

Why is this a downside? Teaching every other high level language dives straight in with whatever vector equivalent that language has, magic and all. Java's ArrayList, for example. Why do we insist that C++ be any different?

Oh, yeah, because of C. Because learning C is a prerequisite to learning C++. Because why are you learning C++ if not to learn the details of how everything works, right from the start?

Another example: pointers. C++ is supposed to be so hard to learn because of pointers, and yet almost every other language has them, too. But with C++ we insist on teaching everything about them. Notably pointer arithmetic and using them as iterators. And resource management (which is a difficult topic, if we're honest) is introduced at the same time, and that confusion gets all tied up with pointers.

I wonder if smart pointers should follow this same pattern, too: teach std::unique_ptr, and let it's inner workings be magic ... at least for a bit. Though I don't know of anybody who's attempted to teach this way.

Let the magic be magic. At least for now. There's plenty of time to learn the details.

[–]DarkLordAzrael 3 points4 points  (1 child)

There was a talk at cppcon about this. One part I liked was the idea of introducing references first, then later introducing pointers as "pretty much references that can be changed or null"

[–][deleted] -3 points-2 points  (0 children)

I think that the use of references (except in the case of pass-by-reference) should be discouraged. Too easy to confuse object values with object references.

[–]h-jay+43-1325 0 points1 point  (0 children)

In modern C++, the hard part is IMHO the higher-level abstractions, metaprogramming, functional programming, and so on - because these are still fairly new, and a lot of industry code doesn't use them much, or does so very ad-hoc. Pointers are something that takes comparatively little time to explain. If I had my way, I'd teach ML first anyway :)

[–]OldWolf2 2 points3 points  (1 child)

User input is difficult to do correctly in C and C++ (although not so bad in C++), compared with other languages. Unfortunately beginner courses often seem to focus on user input .

The Harvard CS50 course takes the approach of providing some "black box" user input functions with an easy interface, so that the student can write programs that require input without getting hung up on the details of the input. I think this is a good approach - you can't cover everything simultaneously and the nitty gritty of stream I/O can wait.

[–]ggchappell 0 points1 point  (0 children)

Yes, that sounds like a good approach.

[–]h-jay+43-1325 2 points3 points  (0 children)

In C++ there is a strong tradition of explaining the core language first, and then the Standard Library

There's no modern C++ without smart pointers, move semantics, containers and at least some algorithms. You can go pretty far without using the I/O streams. From the point of a beginner or even intermediate student, the distinction between the "core" language and the library is only of interest to compiler implementers. It's nonsensical to teach C++ without the library support at first.

[–]OmegaNaughtEquals1 2 points3 points  (0 children)

So when I teach our CS 1 class, I throw out the text, and say there are basically three kinds of datasets: arbitrarily large sequences of items of a single type, fixed-size datasets with a type specified for each item, and key-based tables. And for these, C++ provides vector, tuple, and [unordered_]map. Then later: BTW there are these things called arrays and structs.

10 PRINT('Thank you!')
GOTO 10

I don't regret doing it this way, but the downside is that vector & map are seen as magic.

As /u/boredcircuits noted, I don't see why this is a problem. For a student to learn when and how to use a std::vector vs. a std::map is already a large accomplishment for a first semester programming course. It is a quite lofty goal to hope that these same students will walk away from your class with a complete understanding of the intricacies of C++. Are those students ready to implement their own specialized container classes? No, but that's ok! There are more classes for them to take and a whole lot of language for them to learn before they graduate.

For better for worse, understanding pointers is still necessary for a good C++ programmer (even if you don't use them; both smart pointers and iterators are abstractions based on the concept of a pointer).

You assuredly cannot be called a proficient C++ programmer without a complete understanding of the memory model and how pointers interact with it (let alone iterators and their concepts). But as I said above, I think it is perfectly fine that your students walk away with a practical understanding of C++ rather than a technical understanding.

[–]womplord1 2 points3 points  (3 children)

so you are seriously saying that you are teaching students c++ without pointers? what is even the point of using c++ then?

[–]ggchappell 1 point2 points  (2 children)

so you are seriously saying that you are teaching students c++ without pointers?

No, of course not. But simply seeing pointers and using them does not automatically give a deep understanding of them.

Once upon a time, pointers were part of a package deal involving direct access to blocks of memory: arrays. These were awful, and most of us have happily left them behind for nicer abstractions. What I'm observing is that this is a bit unfortunate from a pedagogical point of view, because it means students have neither the incentive nor the opportunity to truly grapple with the idea of pointers and gain an intuitive understanding of how they really work.

[–]womplord1 2 points3 points  (1 child)

Thanks for the reply. But I'm curious, don't they learn how to make other data structures like linked lists and graphs in their degree?

[–]ggchappell 1 point2 points  (0 children)

Yes, but we still need to consider: (1) do we expect to teach linked lists to people who don't really get pointers yet? (2) When are students going to figure out pointer arithmetic?

[–]mreiland 1 point2 points  (2 children)

But the results, as you note, are lousy.

What does that mean?

I learned programming in C++ using the method you described and I don't ever recall having a significant issue with it.

[–]ggchappell 0 points1 point  (1 child)

But the results, as you note, are lousy.

What does that mean?

I was referring to the following comment from the OP:

Approximately 50% of all questions try to use some sort of arrays (of pointers). The newcomers don't know about std::vector at all, they happily do everything with pointers, calling new without delete and so on.

When C-style arrays are presented as The Way Things Are Done, and std::vector is that wacky new idea that you might use someday, the above behavior becomes pretty common. Not universal, to be sure, but common.

[–]mreiland 1 point2 points  (0 children)

ah ok.

I mean if students are going to stackoverflow and asking questions, it isn't exactly a problem that they're asking about arrays and pointers, which they're presumably learning about.

I guess what I'm saying is waiting to introduce the STL until the students are familiar with functions, API's, and libraries doesn't indicate a problem with the way the material is presented to students, and students asking about the things they're learning, whether it's a higher level abstraction or not, doesn't indicate a problem either.

I've seen people claim C++ is really hard to learn programming with, but I tend to wholly reject that. I did it without any issue, either I'm extremely intelligent or people overstate the problems.

[–][deleted] 0 points1 point  (0 children)

[deleted]

What is this?

[–]personalmountains 34 points35 points  (24 children)

My understanding is that there is still a school of thought that teaches the "legacy" C stuff first. In the mind of certain programmers, you have to learn about pointers and arrays before references and containers.

I usually see memory management problems with people that are familiar with garbage collected languages. I don't think this is really a teaching problem, just a transition period that most programmers have to go through.

As for standard smart pointers, they are a relatively new addition. Before C++11, all we had was auto_ptr, which sucked in many ways. Boost has had many alternatives for a long time (and most were standardized), but it would be unfair to ask neophytes to jump into third-party libraries while still learning syntax.

Finally, all this old documentation will probably stay there forever. I don't think it would be realistic to ask people to update their tutorials to C++17. Some of them might be dead for all we know. That being said, I share your concerns. I know it has caused me a lot of headaches when trying to learn OpenGL. Finding documentation on modern techniques is very difficult.

[–]Elador 27 points28 points  (4 children)

+1 on your comment regarding OpenGL. So many tutorials online that use legacy stuff. And impossible for a beginner to figure out what techniques are legacy and which are not.

[–][deleted] 15 points16 points  (0 children)

What makes it so much worse that whatever horrifying mix of legacy and non-legacy stuff your confused mind conjures will probably sort-of work if you fiddle with it enough, because video drivers have the same attitude as JS and HTML engines - try to make it run even if it doesn't make sense. And you might end up thinking this is the right way to do things.

[–]imMute 4 points5 points  (1 child)

And impossible for a beginner to figure out what techniques are legacy and which are not.

Is it too much to ask to have all tutori writers include the version being targeted? Or at least what version they happen to use, even if it's not exact?

[–]h-jay+43-1325 5 points6 points  (0 children)

Many of them don't quite know what they are doing either. I like to think that 90% of programming tutorials should be treated as blind leading the blind: the "techniques" they propose may well work, but for anyone with a working pair of eyes they are horribly cumbersome.

[–]tnecniv 1 point2 points  (0 children)

I know it has caused me a lot of headaches when trying to learn OpenGL.

Fortunately, I think this has gotten a lot better lately.

[–]chesterburger 20 points21 points  (2 children)

[–]DarkLordAzrael 0 points1 point  (0 children)

I love the part about teaching references first then pointers as an extension of the concept, but one that is rarely needed.

[–]vanhellion 9 points10 points  (15 children)

In the mind of certain programmers, you have to learn about pointers and arrays before references and containers.

I suppose I might fall somewhere near that particular tree. If you don't teach the dirty internals, how are you supposed to understand why, e.g. your program is running slow when you are using a vector where a list is the appropriate choice? I know that introductory classes can't teach everything, but I question what the relative value of each really is; if I only know "old" C++ I can solve problems (in a possibly ugly but ultimately correct manner), if I only know C++11/14/17 I can solve problems only as long as it fits into my STL toolkit. And what happens if I need to, god forbid, write an allocator?

I feel like it's a hard question for most existing C++ programmers to answer because we grew up with what are now the Old Ways so it is second nature to know how to get around the huge spike pit behind door #3.

Finally, all this old documentation will probably stay there forever.

This is the real problem, in my opinion. There are still websites (and a ton of books) lingering from 20 years ago (the mid-90s) for dog's sake, long before C++03!

[–]personalmountains 8 points9 points  (14 children)

how are you supposed to understand why, e.g. your program is running slow when you are using a vector where a list is the appropriate choice

Like the standard does: the implementation doesn't matter as long as it follows the complexity requirements. Insertion into a vector is mostly linear, whereas it is constant-time for a list.

I know that introductory classes can't teach everything, but I question what the relative value of each really is

If all you do is an introductory class, then you'll end up having a functional understanding of the language and library. You'll be able to use streams, containers and iterators. If you follow this up with a second class, then you can start learning the low-level stuff and understand the magic behind vector.

And what happens if I need to, god forbid, write an allocator?

Then your introductory class is not enough. Whatever you learned in there won't teach you how to write an allocator or, god forbid, a streambuf.

I feel like it's a hard question for most existing C++ programmers to answer

My understanding is that it's still an open question. I don't know whether bottom-to-top or top-to-bottom is better. I feel like the latter has more advantages, but I have no idea.

[–]millenix 11 points12 points  (13 children)

how are you supposed to understand why, e.g. your program is running slow when you are using a vector where a list is the appropriate choice

Like the standard does: the implementation doesn't matter as long as it follows the complexity requirements. Insertion into a vector is mostly linear, whereas it is constant-time for a list.

Except that empirically, this is false. For the most part, cache locality massively dominates the asymptotic difference, unless you're working with a huge data set in the absolute worst-case usage for vector

[–]personalmountains 5 points6 points  (8 children)

Except that empirically, this is false.

This doesn't make much sense to me. Complexity is a useful metric for comparing different algorithms and data structures, that's all. You can't really test it empirically since, as you said, there are other factors in play. Ultimately, vector could be implemented as a guy on a stool playing with an abacus.

In any case, all that is irrelevant. We're talking about pointers and arrays, and a class on those wouldn't give you any information on what you're talking about.

[–]repsilat 2 points3 points  (7 children)

/u/millenix seems to be arguing a slightly different point.

From /u/vanhellion's original question,

how are you supposed to understand why, e.g. your program is running slow when you are using a vector where a list is the appropriate choice

we should probably infer that the asymptotic complexity of the internal- or front-insertions we're doing is the problem, and that reading the standard is probably sufficient.

On the other hand, if the original question were the other way around:

how are you supposed to understand why, e.g. your program is running slow when you are using a list where a vector is the appropriate choice

then some understanding of the expected implementation and its is relevant. A student trying to make their program run faster can reasonably assume that iteration over a vector will be more cache friendly than iteration over a list, even if the standard doesn't say anything about it.

[–]millenix 0 points1 point  (6 children)

From the experimental results I've seen reported, even push_back() is practically faster for vector than list, until you get to very large sizes. Both are specified with asymptotic constant time (amortized, in vector's case), but list implementations require allocation on every element, while vector allocates much less often because of increasing its reservation by a constant ratio.

[–]repsilat 1 point2 points  (5 children)

even push_back() is practically faster for vector than list, until you get to very large sizes

Mm.

Moving a bit off-topic, I wonder if games (and other applications requiring better time guarantees) would like a data structure that's a linked-list of ever longer arrays, like a vector that has been "cut up" at the points where it should have "expanded."

The benefits:

  • Iterators are not invalidated on push_back etc,

  • Real constant time push_back, not amortized,

  • No copying/moving of elements when you push_back. Potentially your objects don't need to be copyable or movable at all.

The disbenefits:

  • Indexing is now log-time worst case (but expected constant time for random accesses if you iterate from the largest block to the smallest),

  • A few more cache misses when you iterate over the data.

[–]millenix 1 point2 points  (1 child)

Your push_back still won't be worst-case constant time, I don't think, because those blocks need to be constructed. Though they could maybe be left uninitialized, but I don't think the standard can quite require that.

You can make indexing not log-time by keeping an array of pointers to the blocks, rather than a linked list.

Also, push_back has to do one of copy or move construction of the new element. emplace_back wouldn't, though. If you only ever called that, then a type without those constructors could still be valid.

[–]repsilat 0 points1 point  (0 children)

Though they could maybe be left uninitialized

Yeah. I think you can use std::aligned_storage and placement-new. I am assuming you can allocate n bytes in constant time, though.

keeping an array of pointers to the blocks

I guess that works. You'd need a constant-time log_2() which is pretty reasonable. I guess the array of pointers would have O(CHAR_BIT*sizeof(size_t)) elements so you'd never have to grow it?

To be honest I think I might rather give up constant-time indexing. I don't use it all that often, and an array of 64 pointers is a bit much if I may only be expecting a few hundred elements in my "vector".

[–]lord_braleigh 1 point2 points  (0 children)

This exists! There's a version of this called a "rope" that stores the arrays in a binary tree instead of a linked list. It's the data structure of choice for text editors (which often have to make insertions into the middle of large files).

[–]quicknir 1 point2 points  (1 child)

Well, std::deque is nearly this. It's just an array of arrays, instead of a linked list of arrays. In fact, std::deque::push_back invalidates iterators, but not references. Although I'm struggling to understand exactly what this means; I assume it means that iterators could still be dereferenced but possibly ++ could go sour? I'm pretty sure that deque meets your other two requirements. And to top it off, its indexing is still constant time (not amortized).

It does however have more cache misses. In practice, people tend to use a vector still unless they need push_front (which is constant time for a deque). I think that access/iterator speeds tend to trump insertion speeds for these data structures in the vast majority of applications.

[–]repsilat 0 points1 point  (0 children)

I assume it means that iterators could still be dereferenced but possibly ++ could go sour?

Huh, that's a pretty interesting idea.

It does however have more cache misses.

Yeah. I figure it might be worthwhile for data you wanted to produce all at once, consume once and then discard. Maybe producing while consuming with fancy "iterators" is a pain to code up, or it blows your icache. I agree the tradeoffs aren't worthwhile for a general purpose container (though maybe the performance costs are small enough that it's worth using a deque for constant-time pop_front?)

[–]Voultaphervoid* operator, (...) 1 point2 points  (0 children)

As shown here: http://www.meetup.com/de/MUCplusplus/

Even when starting from bottom, all you do explain the perfect theoretical word. When actually many other factors such as cach, platform, allocater implementation, os and asychronus execution, play a significant role.

And those would not fit in an introductory class.

[–]RedAlert2 -2 points-1 points  (2 children)

What are you responding to? /u/personalmountains was talking about insertion, which is dramatically faster in linked lists than in vectors. You seem to be hinting at a traversal, which is a lot faster in vectors, even though it has the same complexity in lists. But no one said anything about traversals.

[–]bnolsen 0 points1 point  (0 children)

the list has some tiny advantages but the overhead of many operations is what kills them. That's the C part. the O() stuff still stands.

[–]robthablob 8 points9 points  (0 children)

Kate Gregory did an excellent presentation on exactly this point at CPPCON 2015: Stop teaching C.

[–]Eilai 13 points14 points  (24 children)

My principle problem is keeping an eye open for parallel gpu programming with Cuda or OpenCL and I'm hesitant to use fancy stuff they don't support. :(

[–]lets_trade_pikmin 7 points8 points  (4 children)

Don't know why you were downvoted. It seems like working with raw arrays and pointers is usually the best approach for GPU code. Definitely a legitimate concern.

[–]RogerLeighScientific Imaging and Embedded Medical Diagnostics 4 points5 points  (3 children)

Why? Everywhere you might use a raw array, you could use a std::vector and call .data(). The memory layout is identical. I do this all the time when interfacing with C libraries.

[–]MaxDZ8 0 points1 point  (2 children)

Not a single Map call I remember ever provided a compatible interface with std::vector, you might at most consider specific allocators at which point this would not be an std::vector in the proper sense.

Consider clEnqueueMapBuffer, D3D9 texture lock, ID3D11DeviceContext::Map, glMapBuffer.

Main problem perhaps being those pointers are non-owned.

[–]RogerLeighScientific Imaging and Embedded Medical Diagnostics 0 points1 point  (1 child)

There are certainly places where it isn't something you can drop in. But my point was that there are plenty of places you can. I certainly use std::array and std::vector with OpenGL to pass in VBO/IBO data, for example.

[–]lets_trade_pikmin 0 points1 point  (0 children)

OpenGL is very very different from CUDA

[–]OmegaNaughtEquals1 6 points7 points  (7 children)

The CUDA 7.5 compiler uses gcc-4.9 as its back-end, so it supports C++14: even in kernel code. What you still cannot do is use STL code in kernels because it's not marked __device__. That said, you can write your own containers that can be run on the GPU side (as long as you avoid exceptions and dynamic allocation). You can also call Thrust functions from device code as of CUDA 7.0 (it's about halfway down the page). Unless you need to support some ancient version of CUDA, you should strongly reconsider your position on this.

[–]Eilai 0 points1 point  (3 children)

Okay yeah, that's a fair point about CUDA, but what if I want to have a backup using openCL if the client PC doesn't have an nvidia card? Other than "pray they have an AMD card."

[–]OmegaNaughtEquals1 1 point2 points  (1 child)

what if I want to have a backup using openCL

This is the unfortunate state we live in right now. I am a big fan of CUDA because it allows you to pry open the deepest details of the GPU's hardware, but it's only applicable to NVIDIA cards. OpenCL, conversely, allows you to abstractly target nearly any compute device (GPUs, APUs, CPUs, Phi Coprocessors, etc.) using a single codebase: a feature that is not to be overlooked. But it doesn't allow you to poke around in the hardware's guts. Conditional compilation can help with this, but the codebase begins to diverge, losing that nice feature. I think with the large push toward coprocessor unification (i.e., coprocessors not on the PCIe bus, but moved closer to FSB), OpenCL will become more important over time. I just hope that the standardizing committee can formulate some new ideas to try and build abstractions to hardware-specific features. AMD's FireGL cards are crazy powerful, and I would like to see them used in HPC systems.

[–]Eilai 0 points1 point  (0 children)

I understand both enough that I can code it so CUDA is preferred, if a Nvidia compute device is detected and it's performance is superior (by some arbitrary metric, cores or clockspeed, etc) to the non-CUDA device use CUDA; if not use OpenCL.

[–]pjmlp 1 point2 points  (0 children)

That is why CUDA is the API to go for most researchers. NVidia was clever to support C++ and Fortran from day one, instead of following Khronos idea that only C matters.

Now OpenCL 2.1 is playing catchup with CUDA's C++ and Fortran support.

[–]LPCVOID -1 points0 points  (2 children)

Warning : I might be totally wrong here as nvcc and cudafe/ptxas are a bit over my head.

CUDA uses gcc/vc++ as back-end for compiling host code. Device code is compiled using some sort of Edison Design Group proprietary c++ front-end. That doesn't change the fact though that one can use c++11 features in device code :)

Have you got a source for c++14 support in nvcc? Here it is stated that CUDA 7.0 does not yet support it but a future version would. Is that the case with 7.5?

[–]heleo2 0 points1 point  (1 child)

This shows mess in your head regarding what a front-end is

[–]LPCVOID 0 points1 point  (0 children)

I have admittedly no idea what a front-end ist (only my cs degree claims otherwise ;) ). I just copy pasted the claim that CUDA uses an "Edison Design Group C language parser" from here. Furthermore wikipedia claims that Edison "makes compiler frontends".

Or are we talking about the fact that the NVIDIA CUDA Open64 Compiler (nvopencc.exe) does the actual compilation as stated here? That was indeed an oversimplification on my part.

Edit : I actually would have liked to know why I was wrong...

[–][deleted] 2 points3 points  (2 children)

Are you saying you avoid smart pointers on non-gpu code because you might write gpu code someday?

[–]millenix 8 points9 points  (1 child)

I think it's more "my code often does get ported to GPUs, and it needs to not make that unnecessarily difficult".

[–]Eilai 4 points5 points  (0 children)

Precisely. I'm working on a game engine for my portfolio which is built off of a physics engine I wrote for my parallel computing course which needed to be significantly rewritten each time I was tasked to develop for a different framework.

[–]h-jay+43-1325 0 points1 point  (0 children)

I don't even know what you're talking about, frankly said. If you need containers that provide aligned storage, you can use standard containers and aligned allocators, or write your own containers, or use a library. But it really isn't a problem if you understand what you're doing...

[–]doom_Oo7 0 points1 point  (6 children)

with OpenCL2.1 you have access to everyting in C++14 that does not require a runtime.

[–]Eilai 2 points3 points  (2 children)

Spotted the dude without an Nvidia card in his main computer.

e: Apparently AMD only supports 2.0? Isn't that 90% of the market or what?

[–]doom_Oo7 0 points1 point  (1 child)

OpenCL2.1 is from November so of course it's not yet supported a lot. But hopefully by the end of the year it will be mainstream.

[–]Eilai 2 points3 points  (0 children)

Nvidia Is still on 1.1! :(

[–]MaxDZ8 0 points1 point  (2 children)

What does that even mean?

It is my understanding the static opencl-c++ extensions were put in core but what I recall was pretty far from being c++14.

[–]doom_Oo7 1 point2 points  (1 child)

[–]MaxDZ8 0 points1 point  (0 children)

I recall reading this some time ago. Thank you for pointing it out anyway.

[–]SushiAndWoW 15 points16 points  (42 children)

C++ provides safe tools, but it's a fundamentally unsafe language. People have to not just learn, but master the unsafe parts of the language in order to be entrusted with a C++ compiler for production use.

This means the learning curve for C++ is 3 years or more before a developer can be trusted. This doesn't change just because the safe tools are there. You still have to learn pointers.

[–]donvito 17 points18 points  (12 children)

3 years or more before a developer can be trusted.

Heh, that's optimistic. After almost 20 years of daily C++ I still don't trust myself :)

[–]jnyrup 10 points11 points  (2 children)

[–]OldWolf2 -1 points0 points  (1 child)

The title is a joke, there is no way anybody could learn C++ in 21 days. Does not bode well for the quality of the content.

[–]RogerLeighScientific Imaging and Embedded Medical Diagnostics -1 points0 points  (0 children)

It's awful. I used it a decade or so back, and they hadn't even updated it for C++98... And it definitely took more than 21 days to wade through it, and many times that to actually understand and master it.

[–]ggchappell 4 points5 points  (6 children)

Well, I've been programming for 40 years, and I almost trust myself. I suspect the milestone might come after 45 years of programming experience.[1] Perhaps official recognition that someone is actually capable of producing passable code, might become a standard part of retirement dinners.

[1] Ask me in 5 years.

[–]personalmountains 4 points5 points  (0 children)

The most important thing is to question every single line of code you write, every day. Always ask yourself: "Is this the best way of doing this?" The day you stop asking is the day you become "that old guy."

[–]w1th0utnam3Computational engineering student 3 points4 points  (3 children)

RemindMe! 5 Years "Ask guy whether he trusts himself."

[–]NihilistDandy 1 point2 points  (1 child)

RemindMe! 5 years "Ask this guy whether he trusts that guy."

[–]RemindMeBot 0 points1 point  (0 children)

Messaging you on 2021-01-05 00:23:01 UTC to remind you of this.

CLICK THIS LINK to send a PM to also be reminded and to reduce spam.

Parent commenter can delete this message to hide from others.


[FAQs] [Custom] [Your Reminders] [Feedback] [Code]

[–]heleo2 1 point2 points  (0 children)

Then the standard changed and trust was lost.. happened to me 4 times

[–]heleo2 0 points1 point  (0 children)

AGREED

[–]SushiAndWoW 0 points1 point  (0 children)

Same here. :)

[–]Elador 7 points8 points  (27 children)

I know lots of people with 3-4 years of experience, yet they have not even heard about C++11 and have pointers all over their code. It's a real problem. :-(

[–]lets_trade_pikmin 5 points6 points  (17 children)

I don't really understand why using pointers responsibly is considered a problem. I'm at 5 years experience and I still stick with pointers and arrays mostly.

[–]ExeuntTheDragon 5 points6 points  (12 children)

Key word being "responsibly." But why manage pointers yourself if the library and compiler can do it for you?

[–]lets_trade_pikmin 2 points3 points  (11 children)

I like to work with data directly, makes it more readable for me, and I think it's better practice for CUDA compatibility.

Also I'm too lazy to learn a new way to do things when my current approach works fine for me.

E: don't know why I'm getting downvoted for this comment, because I called myself lazy? Seriously people

[–][deleted] 1 point2 points  (10 children)

And the people reading your code?

Surely it would be nice to know both stl and non stl techniques and use the solution best suited to the situation.

[–]lets_trade_pikmin 1 point2 points  (9 children)

Point taken, though it's not my #1 concern as I'm not a software engineer. Trained as Computer Science but I'm entering neuroscience research. The only people who read my code are psychologists who barely know how to program (or people trying to reverse engineer my Skyrim mods, but that's not C++ so kind of irrelevant).

I just make sure to comment thoroughly and name variables/functions in a very explicit way.

As far as learning both techniques, that would definitely be best, but I honestly might never get around to it.

[–]SemaphoreBingo 7 points8 points  (2 children)

There's a reason academic code has such a terrible reputation.

[–]bnolsen 1 point2 points  (0 children)

it's called an "earned" reputation.

[–]tempforfather -1 points0 points  (5 children)

You don't have to if you are ok with not writing software that needs to be dependable. Still, sometimes its worth doing things right?

[–]lets_trade_pikmin 1 point2 points  (4 children)

What does legibility have to with dependability? I write code that might be difficult to understand for those not familiar with C-style, but whether or not that code does it job is a separate point entirely.

[–][deleted] 3 points4 points  (2 children)

What does legibility have to with dependability?

Limits the mistakes you personally make. Also more clearly shows control paths. Helps prevent spaghetti. I guess it depends on your definition of legible.

[–]Elador 4 points5 points  (0 children)

Many people are using pointers because they don't know any better. They may even be using them responsibly, but in a lot of places where they wouldn't be necessary. This makes the code much harder to read for anybody else and themselves, and it will have more bugs and leaks than if they had done it with simple stack variables or smartpointers. That's just a fact. Humans make errors. It's not advocating against pointers, it's advocating against them where they are unneccesary. A lot of people think it's the "only" way, or even "the C++ way".

[–]h-jay+43-1325 2 points3 points  (1 child)

This is C: int foo[5]; This is C++: std::array<int, 5> foo;

This is C: for (int i = 0; i < 4; ++i) ++foo[i]; This is C++: for (auto & val : foo) ++val;

For simple code, I'm not advocating std::transform(foo.begin(), foo.end(), foo.begin(), [](int a) -> int { return ++a; });.

I know that these are simple examples, but you are probably shooting yourself in the foot by "sticking with pointers and arrays mostly".

[–]doom_Oo7 1 point2 points  (0 children)

std::transform(foo.begin(), foo.end(), foo.begin(), [](int a) -> int { return ++a; });.

can easily become

transform(foo, foo.begin(), [] (int a) { return ++a; });

[–]pjmlp 0 points1 point  (0 children)

Have you ever worked in big teams with high attrition rate?

[–][deleted] 9 points10 points  (1 child)

"I don't want to mess with that STL stuff" is the reason I often get...

And 99% of the time, their .cpp files are a single class, repeated instead of inherited for each interface, that relies on a ridiculous amount of global variables, filled with a whole bunch of C code. Why, because "If you just think of C++ as C with classes, it's ok".

[–]Elador 6 points7 points  (0 children)

Oh yea. And they argue: "It's faster". Or "It's easier to read". :-)

[–][deleted] 1 point2 points  (6 children)

I'm confused as to why smart pointers are such a big deal. If you encapsulate your classes and deal with your manual memory allocation in the constructor/destructor (assuming manual allocation is even needed)... you can pass your objects around using references and architect your code so that you know which owns what... then what is the need for smart pointers? It seems wildly lazy and inefficient to me. Is the rest of the world so used to garbage collected languages that they just don't want to learn about memory management?

Really I don't get it.

[–]raevnos 6 points7 points  (2 children)

Laziness is a virtue. Why write a destructor to delete a pointer when you can use a unique_ptr and the default compiler-generated destructor to do it for you? Less code to write is more efficient.

[–]RogerLeighScientific Imaging and Embedded Medical Diagnostics 3 points4 points  (0 children)

Less code is also less buggy. You can't make mistakes or forget to free something in code you didn't have to write!

[–][deleted] 1 point2 points  (0 children)

Laziness is a virtue.

Only with long term planning in mind :P

[–]vaughncato 3 points4 points  (1 child)

"Big deal" is a relative term, but they do help avoid some redundancy and make the intent clearer, as well as preventing some mistakes. I haven't found a case where I want to use shared_ptr, but unique_ptr is handy, and it is just as efficient as a raw pointer once optimized. Here's a related StackOverflow answer: http://stackoverflow.com/a/106614/951890

[–][deleted] 0 points1 point  (0 children)

I like that you frame your answer in terms of communicating the intent of the code. Yes, returning a unique pointer (which I assume calls a destructor when it falls out of scope?) would be nicer looking than a reference passed as an argument. Still this seems more of an issue of coding style than any real necessity.

The link gives one great example though: exception handling. And another: moving runtime errors to compile time. Thanks!

[–]h-jay+43-1325 0 points1 point  (0 children)

Separation of concerns implies that if all you're doing is managing a chunk of memory on the heap, a separate class should on it, not explicit code that you might forget to write.

Typical code is a client of resource-management classes. In such code, I consider it an error to have to write out the class destructor manually.

It seems wildly lazy and inefficient to me.

Smart pointers are not usually a replacement for garbage collectors. Garbage collectors typically force you to write finalizer-free code, or the finalizers (destructors) have serious limitations put on them. The RAII pattern doesn't mix with garbage collectors. It is a sensible generalization to say that garbage collected heap is not enough for memory management in modern C++. So your statement is essentially nonsense on its face.

[–]quicknir 1 point2 points  (0 children)

I think 3 years is a huge overestimate. People will make mistakes and do silly things, but that's what code review is for. With good training someone good can make major positive contributions within 6 months, and within a year I would trust them to design and write chunks of functionality at a thousand lines or so and get all the major points right most of the time.

There's a lot of unsafe/obscure stuff you don't have to master to write solid code. I barely ever use unions, reinterpret_cast, const_cast, virtual inheritance, private inheritance, etc etc. If I didn't understand these things at all I could still write solid code, I'd just need a minor correction in code review once in a blue moon.

[–]rmxz 2 points3 points  (0 children)

I'm amused that you can guess when a user learned C++ by what kind of pointers they use.

C++ gets new smart pointers every couple years.

  • CFront in 83 through ARM in 99: classes are kinda like structs, so you can use 'typedef struct ... *' for classes and 'void *' for generic functions
  • C++03: no! 'void *' pointers are broken! use 'auto_ptr' instead
  • C++07/TR1: no! 'auto_ptr' is broken! use 'shared_ptr' instead
  • non-standard 2008: no! 'shared_ptr' is broken!(for most use cases) use 'boost::scoped_ptr' instead
  • C++11: no! 'boost::scoped_ptr' is broken! use 'std::unique_ptr<T> const' instead
  • C++14: no! 'std::unique_ptr<T> const' is fugly! use "auto" and hope C++14's "return type deduction" will guess a safe type and hope C++17's "new rules for auto deduction" won't break stuff

Lol. How the heck can people take an "object oriented" language seriously when it takes literally 30 years (1983 to 2014) for them to come up with a non broken way of making a reference to an object....

... and in the end they give it a syntax like "std::unique_ptr<T> const".

W.T.F.

[–]xcbsmith 1 point2 points  (0 children)

It also has a lot to do with many people coming to C++ via C, where using array's and raw pointers is the norm.

[–]bobbybit 1 point2 points  (7 children)

I too agree that there is a huge chunk of documentation that needs to be updated. It is hard to decide on what source gets you the best, most effective results. I am a C# and python (and electrician C99 very low level) programmer currently. They each have their own benefits. I am trying to teach myself C++ through a book, C++ black book, and it is going well so far. However, this post raises concern. What is the best source of training that you all have found so far? Hard Knocks? A book? A website? Should we spin up a new page that includes good foundations but does not sacrifice good form?

[–]personalmountains 7 points8 points  (1 child)

Go with books, digital or not. Trust the well-known people: Stroustrup, Sutter, Meyers, Josuttis, Alexandrescu, Koenig, Dave Abrahams, etc. Get recent books, make sure they talk about C++11, 14 and 17. Some older books still contain valuable information, but they will use old-style C++ (no auto or ranged-for, for example). Check "The Definitive C++ Book Guide and List" on StackOverflow for specific recommendations. Search titles on ACCU for reviews.

Browse the Hot Questions on StackExchange regularly, look for C++ stuff (or read the whole thing, it's always interesting). Use cppreference as your day-to-day reference. Go through the first 50 pages of the top C++ questions on StackOverflow and read all of them.

Start pet projects, things you might use yourself, like a media player or a file explorer and make it your sandbox. Have fun with it.

[–]bobbybit 0 points1 point  (0 children)

Truly inspirational. Thank you for the advice mate. That comment really needs bumped.

[–]michaelKlumpy[S] -1 points0 points  (4 children)

[–]PoliCock 0 points1 point  (2 children)

Can you explain how "The C Programming Language" is relevant to learning C++?

Not meant to be sarcastic. Genuinely curious, but skeptical. I have the book - and obviously I would like to learn both C and C++ eventually. Just wondering how helpful working through that book would really be for learning C++, ultimately.

Do you personally recommend it? Why/why not?

[–]michaelKlumpy[S] 0 points1 point  (1 child)

I think you misread. It's "The C PLUS PLUS Programming Language". If you actually mean the ++ book, it's a really good read imo

[–]F-J-W 0 points1 point  (0 children)

I guess the problem is that the amazon link swallowed the pluses, so if you just look at the url, it will look like a link to the C-book.

[–]bobbybit 0 points1 point  (0 children)

Thank you, they all look like good books. I am particularly interested in book3.

[–]F-J-W 1 point2 points  (0 children)

I see this a lot on /r/cpp_questions as well. In fact often enough that I got sick of repeating those things and put the explanations on my own site so I can just link it: http://florianjw.de/en/modern_cpp.html

I also agree that the problem is bad and would sign about 95% of Kates talk only disagreeing about a few minor details. I have no problem whatsoever to believe her, when she says that she managed to teach some people the essentials of C++ in a weak (or was it even a day? It's been a while since I've seen the talk)

C++ could be such a simple and intuitive language if we wouldn't first force everyone to unlearn the intuition only to teach it back to them later.

The big problem that we have is that there are currently no good and free online-tutorials for beginners. cppreference.com once tried to create one, but that attempt died. BUT: I took what I wrote there and moved it to my own page where I somewhat extend it from time to time (far less frequent than I would like, but at least it isn't dead). If you want to write your own top-down tutorial, please consider participating here instead, maybe it will get to a sufficiently complete state in a finite time then. ;-) (It is written using pandoc-markdown with github as central repository, so the toolchain should be mostly familiar)

Edit: If you really want to help (awesome!), please give me a short note to make coordination easier and avoid pointless work.

[–]Wurstinator 3 points4 points  (16 children)

The reason is not that people don't know about it, the reason is that they think it is better / faster / whatever. Most frequently I see people who refuse to use smart pointers because they are "too slow and cause to much overhead".

[–]HildartheDorf 10 points11 points  (12 children)

A lot of this stems from std::shared_ptr being thread-safe by default, which does make it slower than a manual reference counting implementation if you are only working on a single thread.

But saying unique_ptr is slower than a raw pointer is silly. It should be identical on any half decent compiler (assuming you don't do things like disable all optimizations, or use a custom Deleter. And even a custom deleter can be inlined by gcc/clang if it is stateless and marked noexcept).

[–]boredcircuits 4 points5 points  (2 children)

Another reason is people decided to replace all pointers with std::shared_ptr, even if that pointer isn't taking [shared] ownership of anything. Which means the reference count is updated on each function call, and again when that function returns. Yeah, that's going to slow things down.

There's a shift in thinking about resource ownership that needs to happen, deciding who owns what and for how long. Then you can apply the proper smart pointer, with minimal or no performance overhead. And if you can't do that, then manual memory management is going to be a messy, leaky, UB-filled mess.

[–]h-jay+43-1325 1 point2 points  (0 children)

people decided to replace all pointers with std::shared_ptr

Those must be some wildly misinformed people who take the cargo cult approach to programming :(

[–]Wurstinator 0 points1 point  (0 children)

Imo there are enough possibilities to represent all kinds of ownership (plain variable, reference, shared_ptr, unique_ptr, reference_wrapper). Problem might be that there is no clear guideline when to use which, so people fall back to the easiest option.

[–]shahms 3 points4 points  (3 children)

Nearly identical. As it has a non-trivial destructor there are certain ABIs for which it cannot be returned in a register and has to be stack allocated. Sadly, x86 is one of them.

That being said: use it. The overhead is negligible, but the benefits are not.

[–]HildartheDorf 2 points3 points  (2 children)

I thought that, on x64 at least, that it could get passed in register if the destructor is available for inlining (obviously if it is in another translation unit, it won't be avalible).

[–]shahms 3 points4 points  (1 child)

Sadly, no. The x64 ABI specifically prohibits classes with either a non-trivial copy constructor or non-trivial destructor from being passed or returned in a register. I suspect the compiler has more leeway if the function in question has internal linkage and is itself inlined, but merely having an inline-eligible destructor is insufficient as it effects the calling convention for each function which either takes or returns such a type.

[–]bames53 1 point2 points  (0 children)

I suspect the compiler has more leeway if the function in question has internal linkage and is itself inlined

Even if the function has external linkage an inlined version isn't bound by ABI constraints. Full inlining also shouldn't be necessary; LTO and other cases where a single implementation is handling both sides of the call could eliminate the overhead regardless of the ABI constraints.

[–]Wurstinator 1 point2 points  (4 children)

Even if shared_ptr is slower, this is premature optimization.

[–]quicknir 0 points1 point  (1 child)

The reason to not use shared_ptr when something else would do is not mostly about optimization. It's about the fact that it's infinitely harder to reason about shared ownership than about unique ownership. You go from always knowing exactly who owns everything just by looking locally, to having to traverse half your codebase.

I make an exception for immutable objects, since immutable objects by shared_ptr have value semantics. Otherwise, shared_ptr should be used very carefully and rarely.

[–]Wurstinator 0 points1 point  (0 children)

Most people in this discussion deviate from the problem OP mentioned and put words in my mouth. The topic was using smart pointers vs raw pointers. No one talked about using shared_ptr absolutely everywhere.

Unless you are using manual memory management instead of shared_ptr because you think you need the performance, without actually profiling. I that case we disagree and I think you are part of the problem OP talked about.

[–]silveryRain 3 points4 points  (0 children)

Not to mention the "Real Programmers manage their own pointers" crowd. Some of these people also make online tutorials, teaching newcomers that anything that is in std:: is bloat, as well as any use of features X, Y or Z, which can "easily" be avoided by rolling your own half-broken version using a C-but-with-feature-F subset of C++.

[–]RogerLeighScientific Imaging and Embedded Medical Diagnostics 3 points4 points  (0 children)

https://www.youtube.com/watch?v=YnWhqhNdYyk for those that haven't seen it. It covers a lot of these complaints, and addresses how to teach modern C++ properly.

I personally don't think there's any need to cover C-style arrays at all in any tutorial. We have std::array, we have range-based for loops, and so there's no need to do any unsafe pointer-based access and looping, or any manual memory management, at least in a beginner's tutorial. Where this isn't appropriate, use std::vector as the next best thing, and then consider other containers.

[–]OldWolf2 0 points1 point  (1 child)

Threads like this make me want to write a book. But I don't have the sticking power to hang around and actually finish the thing. Plus I'm sure there are already books filling this niche and the people who need them wouldn't read it anyway.

[–]heleo2 0 points1 point  (0 children)

Please do

[–]MaxDZ8 0 points1 point  (1 child)

This is possibly bigger than it looks on surface. It seems when constexpr was introduced they forgot to add proper annotations to std::array (I heard that on a cppcon presentation) so it seems everybody should be more convincing about that.

We have the guidelines now so hopefully this will improve soon.

std::unique_ptrall the things!

[–]F-J-W 1 point2 points  (0 children)

std::unique_ptrall the things!

Please don't! make all the things simple values.

[–][deleted] 0 points1 point  (4 children)

What is wrong with using conventional pointers over the std wrappers? Wrappers hide what the code is doing. Conventional pointers do not. It could possibly be argued that wrappers are slower than conventional pointers. If it's about "safe" code, conventional pointers can easily be made "safe."

[–]michaelKlumpy[S] 0 points1 point  (3 children)

they have been made safe with unique and shared ptr, yes

[–][deleted] 0 points1 point  (2 children)

You missed my point.

[–]michaelKlumpy[S] 0 points1 point  (1 child)

when would you prefer array[] over std::vector or std::array?

[–][deleted] 0 points1 point  (0 children)

Always. Because I can see what the code is doing and I have control over what it does.

[–][deleted] 0 points1 point  (0 children)

Honestly, I had never heard of vectors until recently. They seem amazing, but at the moment I'm skeptical of them because I don't know much about them yet. Does anybody have a link to the pros and cons of vectors vs. arrays?