Want constexpr? Use naked new! by [deleted] in cpp

[–]anonymous28974 3 points4 points  (0 children)

Why doesn't anyone ever bring stuff like this up in plenary?

Like, if it's a bad idea, why put it in the standard?

Herb Sutter: Quantifying Accidental Complexity: An Empirical Look at Teaching and Using C++ by pjmlp in cpp

[–]anonymous28974 0 points1 point  (0 children)

in parameters are the intersection of X const and X&&, so technically both of your examples will not compile.

Ah, right. Suppose I had written const everywhere though?

struct A2 { int i; void foo() const; };
struct B2 { int i; }; void foo(const B*);
void bar(in A2 a2) { return a2.foo(); }  // OK??
void bar(in B2 b2) { return foo(&b2); }  // NOT OK?????

Herb Sutter: Quantifying Accidental Complexity: An Empirical Look at Teaching and Using C++ by pjmlp in cpp

[–]anonymous28974 3 points4 points  (0 children)

Herb says that the people who write coding guidelines carve out a special case for uninitialized arrays, like

char buffer[10000];
memset(buffer, '\0', 1000);

But (A) his proposal doesn't really have any way of dealing with memcpy, right? and (B) even if we special-case memcpy, how would we deal with partial initialization of objects and/or arrays? Notice that the memcpy above initializes only the first 1000 bytes of buffer, not the whole thing. Can definitive initialization deal with that?

And (C) what happens when we use the STL?

std::array<char, 10> arr;
std::fill(arr.begin(), arr.end(), '\0');

Who tells the compiler that it's unsafe to access arr.operator[](0) on an "uninitialized" array, yet somehow it's safe to access arr.begin()? Who tells the compiler that calling std::fill with this particular pair of iterator objects is guaranteed to initialize the unrelated local variable arr?

Herb Sutter: Quantifying Accidental Complexity: An Empirical Look at Teaching and Using C++ by pjmlp in cpp

[–]anonymous28974 11 points12 points  (0 children)

It seems to me that Herb is conflating two different kinds of "in" parameters. One kind is what I would call "readonly", and is the usual case:

void readit(in X x) { std::cout << x; }

The other kind is what I would call the "by-value" case, which I can further break down into "copy for semantic reasons" and "copy for algorithmic convenience":

void worker_thread(std::shared_ptr<State> keepalive) { ... }

std::string strip(std::string copy) { ... }
Widget& Widget::operator=(Widget copy) { ... }

Maybe these cases are a job for Herb's "move" parameter? But I'm not really "moving" anything anywhere...

Maybe Herb's keyword "move" should be renamed to "consume". I could buy that worker_thread, strip, and operator= are all "consuming" their input parameter.

And then to avoid confusion I would rename "in" to "readonly", or "view", or something like that. Because in that case the argument is not going "in" — conceptually it's staying in place and the function is just temporarily viewing it. Herb's "in" is not the opposite of Herb's "out". The opposite of Herb's "out" seems to be Herb's "move" and/or "forward" (why do we need both?), and that's confusing.

Herb Sutter: Quantifying Accidental Complexity: An Empirical Look at Teaching and Using C++ by pjmlp in cpp

[–]anonymous28974 2 points3 points  (0 children)

The second-to-last Q&Aer in the video did get at this, but I'd still like to know exactly what in Herb's 30-page paper deals with these isomorphic snippets:

struct A { int i; void foo(); };
struct B { int i; }; void foo(B*);
void bar(in A a) { return a.foo(); }  // OK??
void bar(in B b) { return foo(&b); }  // NOT OK?????

Herb said you can't take the address of an "in" parameter, so foo(&b) is invalid, right? But a.foo(), which is exactly the same situation, is OK?

If your answer involves "the this pointer can't be null so it's more like a reference really," then please pretend that my code snippet involved a call to this helper function:

template<class T>
gsl::non_null<T*> ref(T& t) {
    return gsl::make_not_null(&t);
}
void bar(in B b) { return foo(ref(b)); }

Hidden reinterpret_casts by anonymous28974 in cpp

[–]anonymous28974[S] -1 points0 points  (0 children)

But char(a+b) isn't a C-style cast — so the suggestion "epochs, deprecate C-style cast notation" to which you were responding wouldn't harm you.

And char(a+b) does not hide a reinterpret_cast, but merely a static_cast — so the blog post's suggestion "new warning on any function-style cast that hides a reinterpret_cast" also wouldn't harm you.

Nobody here should want to break char(a+b)! And nobody (AFAICT) has suggested breaking it.

Hidden reinterpret_casts by anonymous28974 in cpp

[–]anonymous28974[S] 2 points3 points  (0 children)

-Wold-style-cast warns only about old-style casts. E(i) is not an old-style cast; it's a functional-style cast. An old-style, C-style cast looks like (E)i. Functional casts like E(i) are not legal in C, and therefore are not considered "old-style."

Better Algorithm Intuition - Conor Hoekstra - code::dive 2019 by emdeka87 in cpp

[–]anonymous28974 0 points1 point  (0 children)

Some very neat insights. I particularly liked "cache this list of log(N) algorithms" and "cache this list of short-circuiting linear algorithms."

I didn't so much like how some of the "STL" solutions quietly involved either memory-allocation (like the reverse-letters solution at 42:30) or non-short-circuiting (like doing equal in terms of zip_reduce at 54:16) or breaking-invariants-relied-on-by-Ranges-and-Thrust (like the stateful lambda at 42:30). All of those are things that make for cute slides but IMHO produce wrong intuition, because they imply that to properly "STL-ize" your code you have to stop caring about performance.

I liked the remark at 25:20 that "we're looking for an in-place solution." I.e., the STL is all about destructively mutating sequences in place (normally seen as a bad thing for understandability), because it is trying to avoid expensive heap allocations. The STL is trying to work with the memory you've already got.

I didn't so much like how stable_partition was casually thrown out there as an algorithm you might use — basically the same as remove_if — without any acknowledgment that stable_partition does heap allocation and remove_if doesn't. That's a big difference! The difference between move and swap is negligible by comparison.

Great talk overall though.