2

Let's say we have a variable

std::optional<T> x;

of type std::optional<T> for some type T. If I want to call a constructor of T inside x and initialize it, I can call the member function std::optional::emplace. As I know, this member function checks if the instance already exists (that is, bool(x) evaluates to true), and if that's the case, it first destroys the previously constructed instance and then re-initialize it using the constructor and arguments provided.

I'm wondering if it is possible to do only initialization w/o this checking and destruction. When dealing with std::optional, it seems quite common to first check if the instance exists and then do some separate works depending on the result. Therefore, it seems reasonable to argue that quite often when we need to emplace an instance inside std::optional, we already know that it does not contain any initialized instance. Hence, it seems reasonable to provide an "unsafe-version" of std::optional::emplace that does not check and destroy the previous instance, but std::optional does not have such a member function. Is there any way to accomplish this?

Perhaps, if there are some guarantees about memory layout for std::optional (which I guess not), then I can call placement new operator directly...

Junekey Jeon
  • 1,496
  • 1
  • 11
  • 18
  • I would hope a decent optimizing compiler would be able to remove these useless checks. Did you check if this check is still in after optimizing? – JVApen Mar 10 '17 at 17:17
  • @JVApen Seems often it indeed does, but not always, especially when construction is done inside another function (which the compiler failed to inline). – Junekey Jeon Mar 10 '17 at 17:19
  • Have you already tried adding assertions on it being empty? – JVApen Mar 10 '17 at 17:23
  • @JVApen Of course. It seems `std::optional::emplace` itself seems often isn't inlined, resulting useless double check. – Junekey Jeon Mar 10 '17 at 17:38
  • How about the "in place" constructor `constexpr explicit optional( std::in_place_t, Args&&... args )` ? – Jarod42 Mar 10 '17 at 17:42
  • 4
    BTW, I doubt that the check is the bottleneck of the application. – Jarod42 Mar 10 '17 at 17:43
  • @Jarod42 You mean, use placement `new` for the `std::optional` instance directly to call the constructor you mentioned, right? Sounds good! And for the other comment: well, of course not a bottleneck, but it's annoying, isn't it? – Junekey Jeon Mar 10 '17 at 17:53
  • It sounds like you're trying to optimize your program. If that's the case, I highly recommend using a profile to measure the execution time before attempting to change your code. You may find that the bottlenecks are in unexpected places. – Dominic Dos Santos Mar 10 '17 at 18:04
  • @JunekeyJeon: "*but it's annoying, isn't it?*" How? It doesn't affect the way your code looks or how you would have to write it. It isn't visible at the cite of the call in any way. The only possible effect it could have is performance. – Nicol Bolas Mar 10 '17 at 18:38

1 Answers1

1

No, it's not possible. And no, calling placement new directly onto the memory wouldn't help either since you'd also have to set the flag in the optional indicating that the optional is engaged - and you can't do that externally.

Therefore, it seems reasonable to argue that quite often when we need to emplace an instance inside std::optional, we already know that it does not contain any initialized instance

That's not a strong argument. emplace() is simply a potential optimization on top of operator=, there's no implicit hint in the use of such a function about the current state of the optional.

Also, if your code is structured like:

if (!o) {
    // stuff
    o.emplace(some, args, here);
}

You can rest assured that your compiler will see the duplicate branch between the explicit operator bool() check and the internal emplace() check and collapse them together, so there will not be an extra branch.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • By "guarantees about memory layout" I meant to include the guarantee on the position of the flag variable. And I see no reason not to provide a "hinted" version of `emplace` together with the normal one. A name such as `unsafe_emplace` may well prevent programmers from overusing it, I think. And I looked at the assembly generated and there were double check because the compiler failed to inline `std::optional::emplace` in spite of the maximum optimization flag. – Junekey Jeon Mar 10 '17 at 18:01