Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

* is now deprecated.


This is not even remotely true. Let's say you have:

  std::unique_ptr<foo> m_foo;
How do you pass that into a function that can receive a pointer to foo, but does not require it? The only way to do so is to declare the function to take a foo* and pass it m_foo.get()

In summary, * will never be deprecated until C++ has support for something along the lines of Rust's borrowing and lifetimes.


I can't edit my original comment anymore, so I'll just write a clarification in the reply.

Many people have pointed out that C++17 has std::optional. While that's true, I don't think it'll deprecate raw pointers. I'll try to explain why.

Here's an example you can paste, compile and run:

  #include <iostream>
  #include <memory>

  struct foo
  {
    int thingamajig;
  };
  
  void borrow_optional_foo(foo * borrowed)
  {
    if (borrowed != nullptr)
    {
      borrowed->thingamajig = 42;
    }
  }

  int main()
  {
    std::unique_ptr<foo> owned = std::make_unique<foo>();
    owned->thingamajig = 17;
    std::cout << owned->thingamajig << std::endl;
    borrow_optional_foo(owned.get());
    std::cout << owned->thingamajig << std::endl;
    return 0;
  }
If you run it, it should print:

  17
  42
What would it look like if we wanted to use std::optional and get the same behavior?

  #include <iostream>
  #include <functional>
  #include <memory>
  #include <optional>

  struct foo
  {
    int thingamajig;
  };
  
  void borrow_optional_foo(std::optional<std::reference_wrapper<foo>> borrowed)
  {
    if (borrowed)
    {
      borrowed->get().thingamajig = 42;
    }
  }

  int main()
  {
    std::unique_ptr<foo> owned = std::make_unique<foo>();
    owned->thingamajig = 17;
    std::cout << owned->thingamajig << std::endl;
    borrow_optional_foo(std::make_optional(std::ref(*(owned.get()))));
    std::cout << owned->thingamajig << std::endl;
    return 0;
  }
As you can see, there's a tradeoff involved. On the one hand, you get crystal clear, descriptive type: std::optional<std::reference_wrapper<foo>> is clearly an optional reference to foo.

On the other hand, using it is absolutely atrocious: you have to write borrowed->get().thingamajig as opposed to borrowed->thingamajig, and std::make_optional(std::ref(*(owned.get()))) as opposed to owned.get()

Does it work? Absolutely. Is it crystal clear? Indisputably so. Will it deprecate raw pointers? I really, really doubt it, but that's just my opinion.


This is a great reply and I think it echoes what I've seen written elsewhere. For example, this one by Herb Sutter:

https://herbsutter.com/2013/06/05/gotw-91-solution-smart-poi...


> How do you pass that into a function that can receive a pointer to foo, but does not require it?

Same as you would have a function that can receive an `int` but does not require it. Or a receive a `std::vector` but does not require it.

I surmise that you mean "how do you pass any value (pointer or otherwise) into a function that can receive a value but does require it". And I surmise you have before used pointer indirection to pass that value because it has a conventional sentinel value of 0/NULL/nullptr.

You are correct in that optional values did not have a standardized solution, until C++17 std::optional.

If you don't have C++17, I recommend one of the equivalent third-party implementations:

* https://www.boost.org/doc/libs/1_52_0/libs/optional/doc/html...

* https://github.com/akrzemi1/Optional


std::optional does not support references (std::optional<MyVeryLargeType&>) however unlike boost, so this precludes usage with non-copyable types.


That was decided against for now, though I'm not sure why. [1]

In any case, you can use std::optional<std::reference_wrapper<MyVeryLargeType>>.

[1] https://stackoverflow.com/a/26895581


> In any case, you can use std::optional<std::reference_wrapper<MyVeryLargeType>>.

I would frankly give a negative code review to anyone who would do that.

You go from e.g.

    void my_function(T* foo, T* bar, int baz)
    {
      // ...
      foo->stuff(bar, baz);
    }
to

    void my_function(std::optional<std::reference_wrapper<T>> foo, std::optional<std::reference_wrapper<T>> bar, int baz)
    {
      // ...
      foo->get().call(&bar->get(), z);
    }
this also has more overhead (sizeof(std::optional<std::reference_wrapper<T>>) is twice the size of T* ), and let's not even start talking about calling conventions and the compile time cost of having to include both <functional> and <optional> everywhere.


It's hard to tell, but if T* foo can be NULL (and I assume that it can, though there's no visible indication), your first function is gonna segfault.

It's easy to tell that in the second function.

Second function makes for a better code review.


heh, to say that I originally wrote if(foo) and then replaced it by //... because that was not the point. Also, dereferencing an unset optional is also UB, so it would segfault all the same given the same preconditions.


Yes. The code is equivalent, but it's clear that the second is meant to optional.

The first has no such indication, except hopefully some human-readable comment that the pointer may be NULL.


> The first has no such indication, except hopefully some human-readable comment that the pointer may be NULL.

I don't understand. If the parameter was not meant to be sometimes null it would be a reference; not a pointer. The fact that a pointer is used is the "this may be null" indication.


I am not sure I understand what you mean, but now (c++17) you can use an optional in this case.


This is precisely what std::optional is for.


Not formally, but as you were responding to a comment on style: indeed, you don't really need * that often; there are more powerful, safer options in most cases.

But C++ is a systems programming language and * is still quite useful.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: