-
-
Notifications
You must be signed in to change notification settings - Fork 187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Add copy and move constructors to classes with non-default destructors #1374
Conversation
…r everything that has custom delete operators
…gs/RELEASE_500/final)
…stable/2017-11-14)
(stat_comp_benchmarks/benchmarks/gp_pois_regr/gp_pois_regr.stan, 0.99) |
...
I'm Not 100% on Vari's assignments
It looks like this
vari& operator=(vari&& x) noexcept {
// (1) Val is const and cannot be assigned to
//this->val_ = x.val_;
• (this also applies to the copy assignment) Conceptually, does it make sense to do an assignment to another vari? The val_ member is const so we can't change it, but that just means we can only change adj_. Removing the const on val_ feels like a not small thing to do so I wanted to check whether there's another solution besides removing that const which feels like a possible issue.
• Will there ever be a circumstance where a vari from the var_stack_ will be assigned to a vari from the var_nochain_stack_ ala below? Also the std::move(this) is kind of dumb looking, but in my brain, if something sits in the var_stack_that is assigned from a thing on thevar_nochain_stack_, shouldn't it be moved to var_nochain_stack_`? tbh I'm pretty not sure how to operate on this one.
This is great. We should be moving toward move semantics where possible.
Is there a motivation for assigning vari? I thought we only needed to assign vari* inside a var---once a vari gets created, it's constant other than for the adjoint value during the reverse pass of autodiff. So if you need the signature, can it just throw?
|
After looking at it idt so, since its only used in |
@SteveBronder, closing for now since it's still WIP. The ideas here are awesome and I'm all for it. |
yeah apologies there are like 2-4 PRs that got bungalowed for various reasons. It's fine and good to close this for now |
Summary
TL;DR: Rule of 5 for our classes that's almost always the default constructors defined explicitly
When a class has only a non-default destructor defined C++ will only generate implicit copy constructors while not generating move constructors. Explicitly defining move constructors (even default defined ones) makes C++ not generate implicit copy constructors. So you need them all if you want moves essentially. I was goofing around today and did that for all the classes that have virtual or custom destructors defined. 96% of it was just adding stuff like
But for things like
vari
there are particulars that I'll discuss below. The functions that this touch areMemory Stuff
stack_alloc
vari
var_stack
part of thechainableAllocator
.gevv_vvv_vari
mdivide_left_ldlt_alloc
(3) and (4) are pretty standard imo and I don't see any weirds coming from them, tbh we could probably just delete the empty destructor since eod it is still calling the destructors of it's members
ODE Solvers
@yizhang-yiz I know you folks have to interact with a C API for sundials, does it make sense to move those objects in the class with the default move constructor? We can remove those if you don't want them.
I also deleted the move constructor and assignment operator for
AutodiffStackStorage
andAutodiffStackSingleton
. They are implicitly not created but since the purpose of that class is to be a singleton I think it's better to be explicit.Tests
Part of the WIP is making sure these moves make sense
Side Effects
Usually I say something like "I hope not!" here but this time, Yes! Sort of. If the compiler has an opportunity to use a move for these classes, it can! 99% of the time this is inconsequential since we don't use
std::forward
orstd::move
very often and we also useconst&
for the input of our signatures. But say we had a function like below.Well now we could call that function from a higher scope like
atan(std::forward<Var>(my_var))
. Or ala eigenThough I'm not sure if the eigen one is as interesting
But thats the jist, yes there are side-effects. We'll be able to start thinking about moving memory down just like how RVO sends it up.
I'm Not 100% on Vari's assignments
It looks like this, the members are plain old data so it's fine to just copy them
(this also applies to the copy assignment) Conceptually, does it make sense to do an assignment to another
vari
? Theval_
member is const so we can't change it, but that just means we can only changeadj_
. Removing the const onval_
feels like a not small thing to do so I wanted to check whether there's another solution besides removing that const which feels like a possible issue.Will there ever be a circumstance where a vari from the
var_stack_
will be assigned to avari
from thevar_nochain_stack_
ala below? Also thestd::move(this)
is kind of dumb looking, but in my brain, if something sits in the var_stack_that is assigned from a thing on the
var_nochain_stack_, shouldn't it be moved to
var_nochain_stack_`? tbh I'm pretty not sure how to operate on this one.Checklist
Math issue Update internals to use more modern c++ #1308
Copyright holder: Steve Bronder
The copyright holder is typically you or your assignee, such as a university or company. By submitting this pull request, the copyright holder is agreeing to the license the submitted work under the following licenses:
- Code: BSD 3-clause (https://opensource.org/licenses/BSD-3-Clause)
- Documentation: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
the basic tests are passing
./runTests.py test/unit
)make test-headers
)make doxygen
)make cpplint
)the code is written in idiomatic C++ and changes are documented in the doxygen
the new changes are tested