4

I've been programming C++ for a while now and I'm starting to doubt that the rule use references whenever possible should be applied everywhere.

Unlike this related SO post I'm interested in a different kind of thing.

In my experience the reference/pointer mix messes up your code:

std::vector<Foo *> &x = get_from_somewhere(); // OK? reference as return value
some_func_pass_by_ref(x); // OK reference argument and reference variable
some_func_by_pointer(x[4]); // OK pointer arg, pointer value
some_func_pass_elem_by_ref(*x[5]); // BAD: pointer value, reference argument
some_func_that_requires_vec_ptr(&x); // BAD: reference value, pointer argument

One option would be to replace & with * const like Foo & with Foo * const

void some_func_by_ref(const std::vector<Foo * const> * const); // BAD: verbose!

this way at least the traversals are gone. and me rewriting function headers is gone, because all arguments will be pointers... at the price of polluting the code with const instead of pointer arithmetic (mainly & and *).

I would like to know how and when you apply use references whenever possible rule.

considering:

  • minimal rewriting of function prototypes (i.e.: oh damn I need need to rewrite alot of prototypes because I want to put this referenced element into a container)
  • increasing readability

    • avoid application of * to transform Foo* to Foo& and vice versa
    • avoid excessive const usage as in * const

NOTES: one thing I figured is to use pointers whenever I intend to ever put the element into an STL container (see boost::ref)

I don't think this is something C++03 specific but C++11 solutions are fine if they can be backported to C++03 (i.e.: NRVO instead of move-semantics).

Community
  • 1
  • 1
Alexander Oh
  • 24,223
  • 14
  • 73
  • 76
  • 4
    For whatever it's worth, which is possibly nothing, how I learned is: *only* use pointers when absolutely necessary, whatever that means. Which sounds to me like almost never, so what's the alternative? – ChiefTwoPencils Jul 02 '13 at 09:53
  • 2
    Unlike references, pointers can be null. – doctorlove Jul 02 '13 at 09:55
  • @C.Lang my problem, I often end up putting the thing into a container and therefore I almost never can avoid pointers. and thus I always end up in code that has references and then transforms them to a pointer and vice versa... – Alexander Oh Jul 02 '13 at 09:55
  • Where did you get that "rule" from? You don't have to use references everywhere, only when suitable. – Some programmer dude Jul 02 '13 at 09:56
  • 5
    @Alex, That's because you use containers of pointers. Put *objects* in containers. – StoryTeller - Unslander Monica Jul 02 '13 at 09:56
  • 2
    As for pointers, whenever you use pointers try to use the new smart pointers that came in C++11. And try to minimize your use of pointers as much as possible. – Some programmer dude Jul 02 '13 at 09:57
  • The whole thing is very implementation specific. The question is how many functions are there for this: `some_func_pass_elem_by_ref(*x[5]);` As for your **NOTES**, I do not understand what you mean by "putting an element to STL container". If you want to put an element there, just put it there, not its pointer. But you probably mean something else. – ondrejdee Jul 02 '13 at 09:58
  • I've somehow not faced that issue. Perhaps we have different approaches to doing things :). – ChiefTwoPencils Jul 02 '13 at 10:00
  • I don't want to put `abstract_syntax_tree_of_function` into a container, because it's big and returning it from the container creates a copy instead of sharing it in multiple passes. – Alexander Oh Jul 02 '13 at 10:00
  • @Alex, C++11 smart poitners and move c'tors to the rescue, then. :) Though, come to think of it. Why are taking it out of the container to work with it? – StoryTeller - Unslander Monica Jul 02 '13 at 10:02
  • It seems to me that the question itself should be more implementation specific. I'll try to add more meaningful code to the question, that you can see where in the base design all the pointer to ref traversals come from. – Alexander Oh Jul 02 '13 at 10:16
  • 3
    *"I often end up putting the thing into a container and therefore I almost never can avoid pointers."* - And this is the point where it starts to shed a strange light on this whole question. – Christian Rau Jul 02 '13 at 10:53
  • I am terribly sorry. Your question is well written, touches a matter that makes many people wonder, and I my self am interested in this topic. However, I cannot not notice that this is all about **opinion**, hence I'm voting to close. IMHO, the only answer to the question ("would like to know how and when you apply use references whenever possible rule"): is: "whenever reasonable", in its full uselessness. The rest is an opinion and case-specific. Please materialize the actual problem description and reask about specific case that bothers you! – quetzalcoatl Jul 02 '13 at 11:51

3 Answers3

7

When should I use references in C++?

When you need to treat a variable like the object itself (most cases when you don't explicitly need pointers and don't want to take ownership of an object).

I would like to know how and when you apply use references whenever possible rule.

whenever possible, except when you need to:

  • work on the address (log the address, diagnose or write custom memory allocation, etc)
  • take ownership of parameter (pass by value)
  • respect an interface that requires a pointer (C interoperability code and legacy code).

Bjarne Stroustrup stated in his book that he introduced references to the language because operators needed to be called without making a copy of the object (that would mean "by pointer") and he needed to respect syntax similar to calling by value (that would mean "not by pointer") (and thus references were born).

In short, you should use pointers as little as possible:

  • if the value is optional ("can be null") then use a std::optional around it, not a pointer
  • if you need to take ownership of the value, receive parameter by value (not a pointer)
  • if you need to read a value without modifying it, receive parameter by const &
  • if you need to allocate dynamically or return newly/dynamically allocated object, transmit value by one of: std::shared_ptr, std::unique_ptr, your_raii_pointer_class_here - not by (raw) pointer
  • if you need to pass a pointer to C code, you should still use the std::xxx_ptr classes, and get the pointer using .get() for getting the raw pointer.

one thing I figured is to use pointers whenever I intend to ever put the element into an STL container (or can I get rid of this?)

You can use Boost Pointer Container library.

utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • Yes. Pointers are very cheap (that is why they were born), but hard to be handled properly (lifetime, ownership issues, etc). Especially in large projects. They also need an experienced programmer and an experienced user for that matter to read a code. Thus, the advise to avoid them when possible. – SChepurin Jul 02 '13 at 12:02
  • Surely `std::optional` is only useful if you wish to pass by value. What if you want to pass by optional reference? – John McFarlane Sep 24 '13 at 16:44
2

IMHO the rule stands because raw pointers are dangerous because ownership and destruction responsibility becomes rapidly unclear. Hence the multiple encapsulations around the concept (smart_ptr, auto_ptr, unique_ptr, ...). First, consider using such encapsulations instead of raw pointer in your container.

Second, why do you need to put pointers in a container ? I mean, they're meant to contain full objects ; they have an allocator as template argument for precise memory allocation after all. Most of the time, you want pointers because you have an OO-approach making heavy use of polymorphism. You should reconsider this approach. For example you can replace:

struct Animal {virtual std::string operator()() = 0;};
struct Dog : Animal {std::string operator()() {return "woof";}};
struct Cat : Animal {std::string operator()() {return "miaow";}};
// can not have a vector<Animal>

By something like this, using Boost.Variant :

struct Dog {std::string operator()() {return "woof";}};
struct Cat {std::string operator()() {return "miaow";}};
typedef boost::variant<Dog, Cat> Animal;
// can have a vector<Animal>

This way when you add a new animal, you inherit nothing, you just add it to the variant.

You can also consider, a little bit more complicated, but far more generic, using Boost.Fusion :

struct Dog {std::string talk; Dog() : talk("wook"){}};
struct Cat {std::string talk; Cat() : talk("miaow"){}};

BOOST_FUSION_ADAPT_STRUCT(Dog, (std::string, talk))
BOOST_FUSION_ADAPT_STRUCT(Cat, (std::string, talk))

typedef boost::fusion::vector<std::string> Animal;

int main()
{
    vector<Animal> animals;
    animals.push_back(Dog());
    animals.push_back(Cat());

    using boost::fusion::at;
    using boost::mpl::int_;

    for(auto a : animals)
    {
        cout << at<int_<0>>(a) << endl;
    }
}

This way you do not even modify an aggregate like variant nor the algorithms on animals, you just need to provide a FUSION_ADAPT matching the used algorithms prerequisites. Both versions (variant and fusion) let you define orthogonal object groups, a useful thing you can not do with inheritance trees.

lip
  • 670
  • 3
  • 9
  • 1
    Even without c++11. I wouldn't recommend auto_ptr. Better get boost. – StoryTeller - Unslander Monica Jul 02 '13 at 10:04
  • The first two sentences don't make much sense: smart pointers can be null – R. Martinho Fernandes Jul 02 '13 at 10:28
  • 1
    Raw pointers are dangerous not so much because they can be null, but because there is no ownership or lifetime management. – syam Jul 02 '13 at 10:29
  • actually lifetime management is not a problem here. the lifetime of every object is till termination anyway. it seems http://www.boost.org/doc/libs/1_52_0/boost/ref.hpp will remove a lot of problems, by containers holding placeholders that are implicitly convertible to references. – Alexander Oh Jul 02 '13 at 10:33
  • @Alex I like the boost::ref idea, but can not manage to compile a running example using `vector>` (with inheritance to dog and cat). My compiler do not like the idea of `push_back` boost::ref on cats or dogs... Can you manage to compile a full example ? I'd like to enhance the answer. – lip Jul 02 '13 at 10:43
  • @lip I think that the more modern solution is to use handles (handle-body idiom) instead of references and pointers. please keep the answer as there are many small solutions that reduce the use of pointers and not everything needs to be solved the same way. As for my problem as I'm noting using pointers for polymorphic reasons, but because ownership of the type is somewhere else and the lifetime of my containers are dominated by other data structures. – Alexander Oh Jul 03 '13 at 11:35
  • @Alex The handle/body idiom is an OO design pattern used to hide implementation details from interface... I do not see the link with references of pointers. What do you call handles ? – lip Jul 03 '13 at 15:08
  • @lip: the handle/body idiom uses a forward declaration and a (smart)pointer/reference to make a handle and hides details in the body/impl. hiding is just part of the story. the handle is actually nothing more than a pointer-class, where you can choose what the constructors do."my code" declares copy constructurors private and forces people to use references (legacy). having a handle class makes this obsolete. – Alexander Oh Jul 03 '13 at 17:40
  • @lip: the standard implementation of the handle/body idiom suggested by Sutter is to use a unique_ptr where you have a heap allocated object behave identical as the handle (handle out of scope = body out of scope). using a shared_ptr instead and copying the pointer in the copy constructor will make it behave exactly as a shared_ptr. boost::reference_wrapper similar to handle/body, but the body is user visible *and* conversion operators will actually convert the handle to the body. hence it's a handle to a reference, where the handle gives a unique address when applying `operator&`. – Alexander Oh Jul 03 '13 at 17:56
0

The following ways seem reasonable dealing with this:

  • boost and C++11 have a class that can cheaply be used to store references in a container: Reference Wrapper
  • A good advice is to use the handle/body idiom more often instead of passing around raw pointers. This also solves the ownership issue of the memory that is governed by the reference or the pointer. Sean Parent from Adobe has pointed this out at a talk at going native 2013.

I chose to use the Handle/Body Idiom approach because it gives pointers automatically copy/assign behaviour while hiding the underlying implementation and ownership semantics. It also acts as kind of a compile time firewall reducing header file inclusion.

Alexander Oh
  • 24,223
  • 14
  • 73
  • 76