Reference values in C++

Reference values are a powerful feature of C++ but I find they have one significant detractor.  A developer can not look at an API call and determine if a parameter is being passed by reference or value (VB has the same problem). 

IMHO this is one item that C# got 100% correct.  In C# developers must say a value is out/ref or a compile error results.  Forcing both the API declaration and usage to specify the reference semantics makes code much more understandable.  When you look at an API call there is absolutely no question about the byref/byval/out semantics of a parameter. 

Internally I've met people who are hesitant to use reference parameters in C++ because of the ambiguity.  Not making it declarative in both places meant unexpected behavior could occur in a number of scenarios.  I agree with this statement. 

But hey we're talking about C++ here.  Any C++ problem can be fixed with some macros and a template right?  I thought about this over the weekend and came up with a quick sample.  Note, I haven't extensively tested this sample yet so there may be bugs.  However it gets the base cases right.

The goal of this API is to allow API authors to force developers to be explicit about their ByRef semantics.  It will prevent developers from silently passing a value by ref and hence getting unexpected behavior.  Failure to do so will result in a compile time error.  Also there is a minor bit of indirection overhead for debug mode but in retail this will compile out to normal code. 

#ifdef DEBUG

template <typename T>
class ByRefType
    explicit ByRefType(T& arg) : m_ref(arg)

    operator T&() const 
        return m_ref;

    ByRefType& operator=(const T& value)
        m_ref = value;
        return *this;


    mutable T& m_ref;

template <typename T>
ByRefType<T> MakeByRefType(T& expr)
    return ByRefType<T>(expr);

#define ByRef(expr) MakeByRefType(expr)
#define ByRefParam(type) ByRefType<type> 


#define ByRef(expr) expr
#define ByRefParam(type) type&


All well and good.  Now we can attribute byref paramaters with ByRefParam() and force callers to tag it as a ByRef argument.

void SimpleByRef(ByRefParam(int) i, int newValue)
    i = newValue;

void Test()
    int i1;
    SimpleByRef(ByRef(i1), 5);
    SimpleByRef(i1, 6);     // Compiler Error!!!

As said before, this is an initial implementation and I expect updates as I use this in code and find bugs.  Please post back with any you find.

Comments (2)

  1. Mike says:

    Nice but a couple of points:

    1) As we all know macros are to be avoided and I’d argue that there is no need for macros in this case. A good compiler will optimize something like SimpleByRef(MakeByRefType(i1), 5); to the point that the generated code looks as if it was a normal & param.

    2) You really need to overload the & (address-of) operator in ByRefType class otherwise you’ll end up with some nasty bugs. This is especially important if you keep using the macros which will result in different behavior between debug and release.

  2. Mike,

    1) Yes, Macros are evil (yet so addictive).  In this case though the goal was to enforce declarative support of reference parameters with no shipping overhead. I agree the overhead is minimal and hopefully removed in RET but I can’t guarantee it.  I’ve worked with many devs who are nervous/hestitant about adding compile time error checking that is built into RET code.  Therefore when I design such a pattern I only enforce it in DEBUG builds.  

    IMHO DEBUG only is good enough.  True you don’t ship DEBUG builds but code shipped at some point was built with DEBUG.  Also RET is hard enough to debug without having to step through extra macros/templates.

    2) Yes this would need to be done in order to avoid the semaintcs you pointed out.  

Skip to main content