Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
cheetah7071
Oct 20, 2010

honk honk
College Slice

Twerk from Home posted:

Also, I thought that compilers were smart enough to automatically use the move constructor if you pass a temporary into the constructor and a move constructor exists? I'll be more careful about make_unique for sure.

ah yeah you're right that it would use the move constructor if one exists, and the copy constructor if it didn't. I was just trying to emphasize that two Derived objects were being constructed: one passed as an argument to make_unique, and one inside the body of make_unique

Adbot
ADBOT LOVES YOU

Xerophyte
Mar 17, 2008

This space intentionally left blank
I would in general recommend worrying less about using rvalue references and doing moves everywhere. Moves are specifically there for when you need to pass ownership of types that are very large. A breakpoint of 128 bits/2x machine width is a common heuristic but you probably don't need to care until you're in the kbs.

It is rare that you need to pass ownership of very large types. Most classes you write will not need to implement move semantics, most functions (and constructors) you write will not take rvalue references. The exceptions are generally around collection types, classes that own collection types, and classes (like unique_ptr) that delete their copy constructor and copy assignment so they can only be moved.

Specifically: unique_ptr is not a very large type, it consists only of a single pointer. You should never pass it by reference. Pass unique_ptr by value if you want to pass ownership, or pass a (const) reference or maybe a pointer to the underlying type if you do not want to pass ownership. This is usually a good way to treat other classes that cannot be copied.

Twerk from Home posted:

Also, I thought that compilers were smart enough to automatically use the move constructor if you pass a temporary into the constructor and a move constructor exists? I'll be more careful about make_unique for sure.
The compiler (E: the language spec, really) will do this just fine when you pass things by value. You can for instance do
C++ code:
  // Constructor
  Consumer(std::unique_ptr<Base> inputBase) : myMember{std::move(inputBase)} {};

  ...

  // Can be called as both
  auto consumer0 = Consumer{std::move(basePtr)};
  auto consumer1 = Consumer{std::make_unique<Base>()};
The make_unique:d temporary will correctly be moved.


Edit: for the use case at hand

Twerk from Home posted:

In my specific case, to get concrete, I have something that's processing streaming data, and I don't want to put the whole "is it compressed? We support multiple types of compression. Is it even coming from a file?" logic into the Consumer. Instead I want to pass in a ready-to-go std::istream, and I do want ownership of it to move to the consumer. This way we can have the logic to turn paths or sockets or whatever into working istreams in one place, and the different ways to actually consume that data in another.
1: Moving ownership of istreams is ... fraught, and not generally possible. Their move constructor and move assign are protected for good reasons, and trying to work around that by making a unique_ptr to the stream is in general a footgun. What if you want to take piped input from cin, for example?

Options:
- Make consuming data a function call that takes a reference to the stream, rather than have a persistent Consumer object that takes ownership. Having a Consumer class can be an implementation detail, one that doesn't need ownership.
- Just read data to buffers, then pass buffers to Consumers.
- Manage the lifetimes elsewhere and just let the Consumer take a reference to the stream, if you really have to.

Depends on the particulars of your situation and data, I suppose.

2: Not sure how relevant this is but be aware that extending the streaming libraries is as I understand it quite tricky if that's a thing you think you might need. You need to create a class derived from std::basic_streambuf, then construct a std::istream from that class. It's not straight-forward.

Xerophyte fucked around with this message at 07:33 on Nov 16, 2022

Xarn
Jun 26, 2015

Ihmemies posted:


code:
int greatest_v2(int* itemptr, int* endptr) {
    int i = 0;
    int largest = *(itemptr + i);

    while (*(itemptr + i) < *(endptr)) {
        if (*(itemptr + i) > largest) {
            largest = *(itemptr + i);
        }
        i++;
    }

    return largest;
}

When I was teaching programming, I would spend lot of time telling students to read their code as they wrote it, rather than what they think they wrote, or what they wish it did. If you actually read the code, the issue is obvious.

Xarn
Jun 26, 2015

cheetah7071 posted:

The syntax for what you're trying to do in Consumer is a little weird because unique_ptr is specifically for cases where only one portion of the code ever needs access to the object. If the calling function constructs the unique_ptr and then passes it to Consumer's constructor, then two parts of the code have access to it simultaneously, which breaks the core guarantee of the class, so it doesn't compile. The two options are to use a shared_ptr instead (if you really want to allow both the calling function and the Consumer object to access it) or to have the constructor for Consumer instead take the arguments you need for it to construct its own unique_ptr

If you really need to do complicated manipulations on the object in the calling function, so passing an actual pointer to a Base object is really needed, and also you really want the funcitonality of unique_ptr, you do have an option, but it's kind of ugly. You can do something like:

C++ code:
struct Consumer {
	Consumer(Base* po_base) : myMember(po_base) {};
	std::unique_ptr<Base> myMember;
}
In this version, the constructor takes a raw pointer, builds a unique pointer around it, and then assumes responsibility for it. If you wanted to get really fancy you could do something like:

C++ code:
struct Consumer {
	Consumer(Base*& po_base) : myMember(po_base) {po_base = nullptr;}
	std::unique_ptr<Base> myMember;
}
where you take control of the pointer and delete the caller's copy of it to really emphasize that it's yours now

this is still kind of dangerous because if the pointer refers to a stack-allocated object this doesn't do at all what you want and in 99% of cases it'll just be easier to avoid wanting to do this than to try to make it work

This is all so wrong, holy poo poo.

C++ code:
void sink(std::unique_ptr<Data> ptr);

// later on

sink(std::move(data));
is a very common pattern and does not have the Data instance accessible from multiple locations, data in the caller will be nullptr after sink returns. For classes, taking unique_ptrs of dependencies is like the most basic dependency injection you can do, and they definitely do not need to know how to construct their dependencies.

------------------

The reason why the move needs to be here

C++ code:
struct SomeType {
    SomeType(std::unique_ptr<Foo>&& f, std::unique_ptr<Bar>&& b):m_foo(std::move(f)), m_bar(std::move(b))
    {}

private:
    std::unique_ptr<Foo> m_foo;
    std::unique_ptr<Bar> m_bar;
};
is that automatic moves apply only in some circumstances, in return statements (obviously does not apply here), and when dealing with temporaries. Because the arguments to the constructor have a name, they are not temporaries, even if the type has `&&` in it.

-----------------

Finally there is a discussion on whether you should accept unique_ptr (and other move-only types) by value or by &&. I am firmly in the side of value, but using && isn't actually wrong either. You just need to be careful about the difference between

C++ code:
template <typename T>
void sink1(std::unique_ptr<T>&& input);

template <typename T>
void sink2(T&& input);
because they are very different.

cheetah7071
Oct 20, 2010

honk honk
College Slice
Ah well I'll just accept being wrong then. Sorry!

e: the pattern isn't one I've encountered so far and it looks awkward to me to have functions that expect std::move. I can see the advantages though.

ee: that'll teach me for thinking that working with this absolutely massive language for two years has actually taught me anything close to all the ways it's okay to use it lol

cheetah7071 fucked around with this message at 10:01 on Nov 16, 2022

Ihmemies
Oct 6, 2012

Xarn posted:

When I was teaching programming, I would spend lot of time telling students to read their code as they wrote it, rather than what they think they wrote, or what they wish it did. If you actually read the code, the issue is obvious.

Multiple people have said this so far but I still have no idea. I never spot my own mistakes and rarely learn from them so most likely this one will go into the same ever growing pile of mistakes not learned from.

Xarn
Jun 26, 2015

Ihmemies posted:

Multiple people have said this so far but I still have no idea. I never spot my own mistakes and rarely learn from them so most likely this one will go into the same ever growing pile of mistakes not learned from.

Try it line by line:

what does this line do
C++ code:
int greatest_v2(int* itemptr, int* endptr) {
this one?
C++ code:
    int i = 0;
this one?
C++ code:
    int largest = *(itemptr + i);
this one?
C++ code:
    while (*(itemptr + i) < *(endptr)) {
and so on, but this already includes your bug.

cheetah7071
Oct 20, 2010

honk honk
College Slice
Geez apparently the functionality I had in my head for what std::move does is actually what std::forward does; I thought move would only produce an rvalue if the input was an rvalue. That'll teach me for even thinking I understand all the core functionality of this language even after two years. Silly me for thinking a language this complicated is something I'll ever really master self taught

Xarn
Jun 26, 2015
forward is great, a function name given to static_cast<decltype(x)&&>(x) :v:

mmkay
Oct 21, 2010

Ihmemies posted:

Multiple people have said this so far but I still have no idea. I never spot my own mistakes and rarely learn from them so most likely this one will go into the same ever growing pile of mistakes not learned from.

Put every single calculation and/or check in a separate line, catching the value in a separate variable and check it under debugger/print it. Don't use the auto keyword for the types.

giogadi
Oct 27, 2009

All the talk earlier about pointers and inheritance earlier makes me think: it’s interesting how polymorphism and pointers are so fundamentally linked. I.e., if you want a vector of polymorphic objects you’re just expected to do it as a vector of pointers (usually to dynamically allocated memory). Since this pattern is bad for cache usage, it would be nice if it were easier to do polymorphic stuff without each individual object being a dynamic allocation.

I know it’s possible: I’ve written for instance a container that exposes a “vector<Base*>” interface, but internally stores each derived type’s instances continuously in its own separate vector. But this was pretty ugly and difficult. it makes me wonder if there’s a way for a language to encode polymorphism to make something like the above way easier to do, or maybe even the default way to use polymorphism.

nielsm
Jun 1, 2009



giogadi posted:

All the talk earlier about pointers and inheritance earlier makes me think: it’s interesting how polymorphism and pointers are so fundamentally linked. I.e., if you want a vector of polymorphic objects you’re just expected to do it as a vector of pointers (usually to dynamically allocated memory). Since this pattern is bad for cache usage, it would be nice if it were easier to do polymorphic stuff without each individual object being a dynamic allocation.

You need to know the storage size of the objects, and then either have your array have variable-size elements (meaning you can no longer do direct look-up but need to walk it like a linked list) or you need to know the maximum storage size of any derived type.
If you know all the possible derived types, you can use a union.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Xarn posted:

Try it line by line:
I don't think this helps if (as in this case) the person doesn't really get what all the * and & is doing.

It can help to read what you wrote, but if you're just reading "something with the pointer" every line you're not gonna see your bug.

My suggestion to help a beginner would be to read * as "contents of" and & as "address of", unless they are in type declarations in which case they are "pointer to" or "reference to" (ignoring for now the bonkers cases like && in a declaration.)

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

nielsm posted:

If you know all the possible derived types, you can use a union.
std::variant, if you have C++17 available (or absl::variant is an option if not).
Edit: I guess a union is good though if you're doing with derived types, because you can handle it as the base class without needing to know the inner type.

Phobeste
Apr 9, 2006

never, like, count out Touchdown Tom, man
In theory if you have a MyVariant = variant<Base, Derived1, …, DerivedN> you should be able to write a Base& view(MyVariant mv) { return visit(mv, [](auto& elem) -> Base& { return dynamic_cast<Base&>(elem);}); } or something

giogadi
Oct 27, 2009

Here’s a great treatment on the subject of polymorphic vectors:

https://johnysswlab.com/process-polymorphic-classes-in-lightning-speed/

But my main point is to wonder whether there are language features that could make this kind of thing easier - all of these solutions, while clever and effective, take a lot of work to implement

giogadi fucked around with this message at 14:40 on Nov 16, 2022

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

Ihmemies posted:

Multiple people have said this so far but I still have no idea. I never spot my own mistakes and rarely learn from them so most likely this one will go into the same ever growing pile of mistakes not learned from.

How do you pronounce & and * when used as a unary operator?

I recommend "address of" and "value at", respectively. You may have learned reference/dereference, which is what they do but not what it gives you.

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

cheetah7071 posted:

Geez apparently the functionality I had in my head for what std::move does is actually what std::forward does; I thought move would only produce an rvalue if the input was an rvalue. That'll teach me for even thinking I understand all the core functionality of this language even after two years. Silly me for thinking a language this complicated is something I'll ever really master self taught

C is a better language than C++, and more people should use it.

Ihmemies
Oct 6, 2012

Well the solution was to not use anything. Just remove everything :D
Thanks, and sorry for being so bad.

Anyways. I'm still battling an eternal struggle against on how to check for keys in a map. Is this legal?

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

Coming from Python, you'll find exceptions in C++ are intended for more "exceptional" cases.

std::map::find() returns a special value when the key is missing, and doesn't require a recent language version.

Twerk from Home
Jan 17, 2009

This avatar brought to you by the 'save our dead gay forums' foundation.

Xerophyte posted:

Edit: for the use case at hand

1: Moving ownership of istreams is ... fraught, and not generally possible. Their move constructor and move assign are protected for good reasons, and trying to work around that by making a unique_ptr to the stream is in general a footgun. What if you want to take piped input from cin, for example?

Options:
- Make consuming data a function call that takes a reference to the stream, rather than have a persistent Consumer object that takes ownership. Having a Consumer class can be an implementation detail, one that doesn't need ownership.
- Just read data to buffers, then pass buffers to Consumers.
- Manage the lifetimes elsewhere and just let the Consumer take a reference to the stream, if you really have to.

Depends on the particulars of your situation and data, I suppose.

2: Not sure how relevant this is but be aware that extending the streaming libraries is as I understand it quite tricky if that's a thing you think you might need. You need to create a class derived from std::basic_streambuf, then construct a std::istream from that class. It's not straight-forward.

I'm going to have a think about the factoring of this application and see how I can move less poo poo around. What if I want to take piped input from cin? cin is an istream, so I could either pass it in directly, or more likely add cin as a source for a boost filtering_stream, because we have compressed data flowing around. I'm guessing your concern is that it'd be easy to accidentally end up with multiple places reading from cin at the same time and blow my face off? I'm wanting the consumer to manage the stream because one part of the application is opening the multiple files that we're reading from, and the consumers get used in different places to read through those files.


Xarn posted:

Finally there is a discussion on whether you should accept unique_ptr (and other move-only types) by value or by &&. I am firmly in the side of value, but using && isn't actually wrong either. You just need to be careful about the difference between

C++ code:
template <typename T>
void sink1(std::unique_ptr<T>&& input);

template <typename T>
void sink2(T&& input);
because they are very different.

Whether you accept them by value or by &&, don't you have to call std::move where you are calling the function anyway because calling sink1(myFoo) with a std::unique_ptr<Foo> and a sink1 that accepts values will try to call the deleted copy constructor anyway? Hence a need to call sink1(std::move(myFoo)), which makes me feel like I should be accepting a && anyway.

giogadi posted:

Here’s a great treatment on the subject of polymorphic vectors:

https://johnysswlab.com/process-polymorphic-classes-in-lightning-speed/

But my main point is to wonder whether there are language features that could make this kind of thing easier - all of these solutions, while clever and effective, take a lot of work to implement

I'm a little surprised at just how clunky basic dependency injection feels. I get that most C++ work at this point is done in the context of older codebases with a ton of internal libraries available to solve most of these problems, but the language and stdlib itself could absolutely offer some more ergonomic ways to do this.

Ihmemies's example of unordered_map::contains not existing until C++ 20 is such a great example of the C++ stdlib being huge but still missing basics.

Ihmemies
Oct 6, 2012

Dijkstracula posted:

Coming from Python, you'll find exceptions in C++ are intended for more "exceptional" cases.

std::map::find() returns a special value when the key is missing, and doesn't require a recent language version.

OK, I'll just do it with find() then. :sigh:

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Twerk from Home posted:

Ihmemies's example of unordered_map::contains not existing until C++ 20 is such a great example of the C++ stdlib being huge but still missing basics.
I can see why people would be resistant to adding it though, because when it exists it encourages
code:
if (my_map.contains(key)) {
  doStuffWith(my_map[key]);
}
which performs the lookup twice, where with find you're more encouraged to do a single lookup.

Dijkstracula
Mar 18, 2003

You can't spell 'vector field' without me, Professor!

right, OP shouldn't feel that using `if (m.find(k) != m.end()) {...}` is a hack or an antipattern here.

Ihmemies
Oct 6, 2012

Dijkstracula posted:

right, OP shouldn't feel that using `if (m.find(k) != m.end()) {...}` is a hack or an antipattern here.

I know. I was just curious.

This is I think how it should be done:

code:
std::shared_ptr<Chapter> Book::findChapter(const std::string &id) const {
    if (idstore.find(id) != idstore.end()) {
        return idstore.at(id);
    }
    cout << "Error: Not found: " << id << endl;
    return nullptr;
}
Any chance to get language specific color coding for code tag at the forums btw?

Sweeper
Nov 29, 2007
The Joe Buck of Posting
Dinosaur Gum

Ihmemies posted:

I know. I was just curious.

This is I think how it should be done:

code:
std::shared_ptr<Chapter> Book::findChapter(const std::string &id) const {
    if (idstore.find(id) != idstore.end()) {
        return idstore.at(id);
    }
    cout << "Error: Not found: " << id << endl;
    return nullptr;
}
Any chance to get language specific color coding for code tag at the forums btw?

find returns an iterator you can return the value from. You are doing the lookup twice

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Sweeper posted:

find returns an iterator you can return the value from. You are doing the lookup twice
More helpfully if you're not familiar:
code:
auto it = map.find(id);
if (it != map.end()) {
  return it->second;
}
return nullptr;

Sweeper
Nov 29, 2007
The Joe Buck of Posting
Dinosaur Gum
If you are new to cpp bookmark https://www.cppreference.com or add a quick search so you can type “cpp std::map” into url bar thing to lookup docs. I look things up all the time because c++ is inconsistent and many apis work in ways which make sense in the context of the standard, but aren’t intuitive. It’s a great site, bless the maintainers.

Xerophyte
Mar 17, 2008

This space intentionally left blank

giogadi posted:

All the talk earlier about pointers and inheritance earlier makes me think: it’s interesting how polymorphism and pointers are so fundamentally linked. I.e., if you want a vector of polymorphic objects you’re just expected to do it as a vector of pointers (usually to dynamically allocated memory). Since this pattern is bad for cache usage, it would be nice if it were easier to do polymorphic stuff without each individual object being a dynamic allocation.

If you want this (and it's often a reasonable thing to want), then you probably want to create a local memory arena to allocate your objects on. You'd do something like
C++ code:
class Fnord {
  MyMemoryArena m_arena;
  std::vector<Base*> m_basePtrs;

  void createVariousObjects() {
    auto derived0Ptr = m_arena.alloc<Derived0>();
    m_basePtrs.push_back(derived0Ptr);

    auto derived1Ptr = m_arena.alloc<Derived1>();
    m_basePtrs.push_back(derived1Ptr);
  }
}
If you know the max size of the derived classes you can as mentioned do this with a union, or just placement new into an array. Those patterns are more useful if you only need the one polymorphic object and don't want to have to go to the heap for it than if you're going to declare a large number of things. If you're declaring a lot of derived types of disparate sizes then you get better locality with an arena than you do with a vector of unions. Dynamic memory arenas require an initial heap allocation at some point, although you can write stack-based ones on most platforms with some work.

You ultimately need to get a base class pointer or reference to collect the disparate derived values, yes. There's just no such thing as a polymorphic value in C++: a value is the thing it is. Is there a language that has value semantics for complex types, and also somehow allows those values to themselves be polymorphic? I struggle to see how that could be done, but I might be blinkered.

Twerk from Home posted:

I'm going to have a think about the factoring of this application and see how I can move less poo poo around. What if I want to take piped input from cin? cin is an istream, so I could either pass it in directly, or more likely add cin as a source for a boost filtering_stream, because we have compressed data flowing around. I'm guessing your concern is that it'd be easy to accidentally end up with multiple places reading from cin at the same time and blow my face off? I'm wanting the consumer to manage the stream because one part of the application is opening the multiple files that we're reading from, and the consumers get used in different places to read through those files.

My concern is that you said "I want to pass in a ready-to-go std::istream, and I do want ownership of it to move to the consumer." istream has disabled moves specifically to ensure it is not possible to do that, because it is not in general a safe operation. An istream may be a system resource that cannot be locally owned. Moving is only enabled for classes derived from istream that represent something that can be owned and have its ownership transferred, notably ifstreams.

The discussion gave me the impression that you intended to get around this by passing around std::unique_ptr<std::istream>s, which technically will work as long as you only use them for streams that can be moved (or, well, constructed/deleted locally). It smells and I would try to not do that, though.

more falafel please
Feb 26, 2005

forums poster

Ihmemies posted:

Any chance to get language specific color coding for code tag at the forums btw?

[ code=c++ ]
...
[ /code ]

Xarn
Jun 26, 2015

Twerk from Home posted:


Whether you accept them by value or by &&, don't you have to call std::move where you are calling the function anyway because calling sink1(myFoo) with a std::unique_ptr<Foo> and a sink1 that accepts values will try to call the deleted copy constructor anyway? Hence a need to call sink1(std::move(myFoo)), which makes me feel like I should be accepting a && anyway.


Yes, you need to call std::move in both cases. But they have different lifetime semantics.

Given
C++ code:
void sink1(std::unique_ptr<Data>&& input) {
    if (coinFlip()) { throw "Nah"; }
    // use up input
}

void sink2(std::unique_ptr<Data> input) {
    if (coinFlip()) { throw "Nah"; }
    // use up input
}
Calling sink1 will not always zero-out the passed pointer, because std::unique_ptr<Data>&& is a reference and thus the caller's unique ptr instance will only be modified after the coinflip. This is different from sink2 where caller's pointer is always moved-out, because it has to be moved into the function before the coinflip happens.

This is why I prefer taking these types by value, because then I can be sure that when the function exits, no matter the way it does so, my input has been already taken care of and I can't see the old value. This makes it harder to get to inconsistent state.





leper khan posted:

C is a better language than C++, and more people should use it.

:hmmno:

ultrafilter
Aug 23, 2007

It's okay if you have any questions.


Xerophyte posted:

Is there a language that has value semantics for complex types, and also somehow allows those values to themselves be polymorphic? I struggle to see how that could be done, but I might be blinkered.

Haskell type classes? I don't think it's exactly the same idea but it's probably as close as you're going to get.

cheetah7071
Oct 20, 2010

honk honk
College Slice
I'll put up with all the C++ nonsense just to have namespaces and std::vector

more falafel please
Feb 26, 2005

forums poster

Speaking of all the chatter about the bizarre complexities of modern C++, anyone have thoughts on Herb Sutter's new project?

https://www.youtube.com/watch?v=ELeZAKCN4tY

I... kinda like the idea of it. It would be (theoretically) usable alongside current C++ code without any glue poo poo, and it would dramatically simplify the language while keeping (almost) all of the zero-overhead abstraction, and the safety of modern idiomatic C++.

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
None of Herb Sutter's projects have gone anywhere and I don't expect this one to be the exception.

pseudorandom name
May 6, 2007

Stop doing C++
"Yes please give me an RVALUE REFERENCE. Please give me SFINAE" - Statements dreamed up by the utterly deranged.
They have played us for absolute fools.
etc.

OddObserver
Apr 3, 2009

more falafel please posted:

Speaking of all the chatter about the bizarre complexities of modern C++, anyone have thoughts on Herb Sutter's new project?

https://www.youtube.com/watch?v=ELeZAKCN4tY

I... kinda like the idea of it. It would be (theoretically) usable alongside current C++ code without any glue poo poo, and it would dramatically simplify the language while keeping (almost) all of the zero-overhead abstraction, and the safety of modern idiomatic C++.
That's sort of a problem, isn't it? Like if you actually care about safety and are going to put in an effort to switch to a C++ reskin suddenly going all the way to something like Rust starts being attractive?

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

pseudorandom name posted:

Stop doing C++
"Yes please give me an RVALUE REFERENCE. Please give me SFINAE" - Statements dreamed up by the utterly deranged.
They have played us for absolute fools.
etc.

Yes. Return to C

It's honestly a very pleasant language to work with. And it even gets useful features like embed before C++

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Xarn posted:

This is why I prefer taking these types by value, because then I can be sure that when the function exits, no matter the way it does so, my input has been already taken care of and I can't see the old value. This makes it harder to get to inconsistent state.
I ran into a surprise with this on some third-party code that took unique_ptr&& - it's especially painful because it turns out it's not even strongly defined behavior. With one set of optimizations the 'original' std::moved unique_ptr was nullptr after the call, with another it was not (when the function returned without doing anything that forced its rvalue-unique_ptr to be moved).

Which is to say, the piece of code that did this passed unit tests with one set of optimizations and ended up throwing a use-after-free or delete-after-free exception or something the other way because of some deletion-order stuff that relied on the unique_ptr having been destroyed before it got to some other stuff.

Adbot
ADBOT LOVES YOU

Xarn
Jun 26, 2015
I give cppfront even less chance for success than I give to Carbon.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply