0

Say I have a class My<X> with a member X x that it won't modify without being moved/destroyed. I believe that x should be a const X, because the variable shouldn't be mutable throughout the lifespan of My<X>, no matter how its value was given or how it will be gotten. That said, I want the value and its type to be preserved through My, as if perfectly forwarded instead.

(I've penciled in pass-through conversion operators just to show the signatures explicitly. In practice, there may be no operators at all, but methods/functions will still use x as if by conversion with these CV/references.)

template<class X> struct My {
    const X x;
    /* 1. */ operator X && (void) && noexcept;
        //{ return std::forward<X>(std::move(*this).x); }
    /* 2. */ operator const X && (void) && noexcept;
        //{ return std::forward<const X>(std::move(*this).x); }
    /* 3. */ operator X const& (void) const& noexcept
          { return x; }
};

I included #2 but I think I can rule it out. I've looked up const X && enough to remember that it has few interpretations and few use cases. The const here was added, I only want to preserve const if it already was.

As for #1, the call to std::forward<X> should be a static_cast<X &&>, so I believe the type is right. Can I use perfect forwarding this way, or will I have copies or dangling references or something?

(I recently did a lot of tests on CV-qualifications and references, sanity checks really, and discovered some weird behaviors that left me a lot less sure of myself. One is that adding const to an lvalue reference has no effect. In fact, it produces a warning, unlike with std::add_const_t. That may actually be a factor here.)

Can a moving struct perfectly forward a member currently marked const? Is there a reason I shouldn't?

wohlstad
  • 12,661
  • 10
  • 26
  • 39
John P
  • 1,463
  • 3
  • 19
  • 39
  • 1
    Moving-construct a const object actually does copy (unless special (rare) constructor taking `const Object&&`). – Jarod42 Feb 09 '22 at 11:19
  • So the `static_cast(x)` has to make an rvalue and only can by copying, right? What if `X` is already an rvalue, does that fall under `const Object &&`? That's more what I meant. The way I wrote `const X` might not be right for that purpose though. – John P Feb 09 '22 at 11:29
  • 1
    Your `std::forward` should simply be `std::move`. So `/*2*/` might simply be `return std::move(x);`. Problem with `/*1*/` is the `const` which should be dropped to compile (`const_cast`), but doing so would lead to UB if returned object is modified. – Jarod42 Feb 09 '22 at 11:56
  • I'm not sure I understood the reason for making `x` `const` in the first place - and what's the reason for `(void)` in your operators? – Ted Lyngmo Feb 09 '22 at 11:57
  • If you don't want `x` to be modified, make it a private member and don't provide any mutating member functions (and no friends :-). – BoP Feb 09 '22 at 12:30
  • @Jarod42 Are you sure, `std::move(const X &&)` is okay? Bear in mind that the `X` in `X &&` may very well be some `Y const&`. @TedLyngmo Internally, it's meant to be const, period. Think of any reason you would ever need const; that. Now imagine you want to do that on your way to perfect-forwarding the value into a function like `std::forward_as_tuple`. You can compose algorithms/functors that are const with respect to that one variable but can have other mutable state. More in a sec. – John P Feb 09 '22 at 12:36
  • @BoP That sort of relies on you never calling const into question. It wouldn't be a compiler error if a regular method tried to modify it - you're just supposed to know that this class should only have const methods, or that it should avoid modifying certain members from non-const methods... and what, that goes in the comments? "Please don't touch"? In any case, that's a design philosophy, not a response to the technical question I asked. Sorry. – John P Feb 09 '22 at 13:03
  • 1
    `std::move(temporary())` is useless `temporary()` is similar. with `const Y y;` `std::move(y)` returns a `const Y&&` (it is ok, even if `const Object&&` is rarely useful). – Jarod42 Feb 09 '22 at 13:04
  • 1
    `const` member leads to several "issues"/constraint (as not assignable, copy instead of move, ...). Similarly, reference member have some constraint too. Simpler is generally to avoid them. – Jarod42 Feb 09 '22 at 13:07
  • 1
    @John - *"Please don't touch"?* I thought **you** were writing this class and didn't want any users modifiying the value. If you let others modify the class, about the first thing that happens would be for them to remove the `const` so that their new `set_x()` compiles. – BoP Feb 09 '22 at 14:10
  • 1
    _"Think of any reason you would ever need const"_ - Well, `const` member _variables_ are i.m.o. rarely needed or useful. As mentioned above, just make them `private` and don't change them _unless_ it's for moving. If you have a `const std::vector` member for example, it will be copied instead of moved which can be pretty expensive. It's just not very practical. – Ted Lyngmo Feb 09 '22 at 19:55
  • @BoP I'm writing the class, and yes, I don't want users modifying the variable, not from this class anyway. And you're right, a user can circumvent or remove strong typing, assertions, etc. just to pass compilation artificially. In that case the best solution is to remove the user. – John P Feb 09 '22 at 20:24
  • 1
    _" In that case the best solution is to remove the user"_ - Best solution I've heard Today! :-) – Ted Lyngmo Feb 09 '22 at 22:09

0 Answers0