2

I want a function to have a universal reference argument with a concrete type. I want it to be universal reference so I can pass a temporary object when I'm not interested in checking the value stored in it.

Here is the example:

bool bCompareData(const uint8_t *arg_0, const uint8_t *arg_1, size_t &szComparedData)
{
    size_t &i = szComparedData;

    for (; arg_1[i]; ++i)
        if (arg_1[i] != arg_0[i])
            return false;

    return true;
}

And the possible function calls:

bCompareData(Obj.uIdObject.data(), Ref.uIdTargetObject.data(), size_t()) // here we are passing a temporary (rvalue)
/*________________________*/ 
size_t lval;
bCompareData(Obj.uIdObject.data(), Ref.uIdTargetObject.data(), lval) // here we are passing a named variable (lvalue)

Using the above function declarations in the first function call compiler will give an error, if I change "size_t &szComparedData" to rvalue reference "size_t &&szComparedData" it will fail at the second call.

Now what I need is Universal References but I would also like to have a concrete type on my argument and not use templates.

I'm using VC++ U3 2013 compiler.

AnArrayOfFunctions
  • 3,452
  • 2
  • 29
  • 66
  • 1
    doesn't MSVC allow the first one anyway? (bind non-const reference to temporary) – M.M Nov 04 '14 at 19:08
  • It seems like you want that last parameter to be optional. Why not simply provide an overload without that parameter, and call the other version with a local dummy parameter? – François Moisan Nov 04 '14 at 19:09
  • @FrançoisMoisan: Passing a temporary discards the result, so optional sort of makes sense, but passing a temporary also allows a different starting point. I'm not sure optional parameters is really what he wants. – Mooing Duck Nov 04 '14 at 19:11
  • 1
    another option here is to make it `size_t *` , then you can pass `nullptr` (which might be a default value) if you aren't interested in seeing the result – M.M Nov 04 '14 at 19:12
  • 1
    Your implementation is strange btw. It seems that you want `strcmp` or `memcmp` or maybe `std::mismatch`. – Jarod42 Nov 04 '14 at 19:12
  • 1
    @FrançoisMoisan: From the example, the parameter is used both as input and output. – Jarod42 Nov 04 '14 at 19:15
  • @MattMcNabb: What if you want to pass in a non-default value, but don't care about the result? – Mooing Duck Nov 04 '14 at 19:16
  • @Jarod42, You and MooingDuck are right, I missed that. – François Moisan Nov 04 '14 at 19:17
  • A duplicate - sorry. – AnArrayOfFunctions Nov 04 '14 at 19:25
  • 1
    @Jako some more ideas here, http://stackoverflow.com/questions/2816293/passing-optional-parameter-by-reference-in-c – M.M Nov 04 '14 at 20:06

3 Answers3

5

Universal references only work with templates and type-deduction by definition. You can only use SFINAE to limit the reference to a specific type:

template< typename S >
typename std::enable_if<
    std::is_same<typename std::decay<S>::type,size_t>::value,
bool>::type
bCompareData(const uint8_t *arg_0, const uint8_t *arg_1, S&& szComparedData)
{
    // ...
}

or, alternatively, you need to overload your method if you really need to avoid templates:

bool bCompareData(const uint8_t *arg_0, const uint8_t *arg_1, size_t& szComparedData)
{
    // ...
}

bool bCompareData(const uint8_t *arg_0, const uint8_t *arg_1, size_t&& szComparedData)
{
    // ...
}
Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • The function modifies the input, so matching `const size_t` variables would be an error. (Besides, I think your overloads create ambiguities) – Mooing Duck Nov 04 '14 at 19:08
  • Your overload version doesn't work as the function modifies the lvalue – M.M Nov 04 '14 at 19:10
  • @MooingDuck Yeah, but is `A&` and `A&&` unambiguous and works for all cases? But maybe here it's OK as the question does limit it to either modifyable or temporary values, excluding constant values. Unusual but possible. – Daniel Frey Nov 04 '14 at 19:10
  • @DanielFrey: Yeah, `A&` and `A&&` are unambiguous; it's `const&` and `&&` that create ambiguity, they both accept temporaries. – Mooing Duck Nov 04 '14 at 19:12
  • @MooingDuck D'oh, of course. Thanks. (and +1 for your answer) – Daniel Frey Nov 04 '14 at 19:14
2

Can't have a universal reference and not use templates, as "universal reference" is a template thing. What you want to is to be able to pass either an lvalue or an rvalue, which can be done without templates.

The correct tool here is overloads. And amusingly, this only requires a very simple overload.

bool bCompareData(const uint8_t *arg_0, const uint8_t *arg_1, size_t &szComparedData)
{
    size_t &i = szComparedData;

    for (; arg_1[i]; ++i)
        if (arg_1[i] != arg_0[i])
            return false;

    return true;
}
bool bCompareData(const uint8_t *arg_0, const uint8_t *arg_1, size_t &&szComparedData)
{ return bCompareData(arg_0, arg_1, szComparedData);}

This works because && in a function parameter list tells that this function parameter is constructed from an rvalue, but all named values are themselves lvalues. Since it has name, it's an lvalue, and you can simply pass it to the existing function.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
0

Or what I did later, which I believe isn't the best solution:

template <typename T = size_t>

inline bool bCompareData(const uint8_t *arg_0, const uint8_t *arg_1, T &&arg_2 = size_t())
{
    arg_2 = 0;

    extern bool _bCompareData(const uint8_t *, const uint8_t *, size_t &); //compare data

    return _bCompareData(arg_0, arg_1, arg_2);
}

C++ is a very strange language. I mean both 'rvalue' & 'lvalue' references have the same size and properties so how did they became different types.

AnArrayOfFunctions
  • 3,452
  • 2
  • 29
  • 66
  • It doesn't require templates, though that is one way of solving the issue. – Mooing Duck Nov 04 '14 at 19:11
  • lvalues and rvalues do not have the same properties. For instance, an rvalue cannot be assigned to, or passed by mutable reference. `char[12]` and `std::vector` might have the same size sometimes, but one would hardly refer to them as the same type. – Mooing Duck Nov 04 '14 at 19:14