Using template argument from universal reference as const ref

258 views Asked by At

My actual usecase takes a few more arguments but it simplifies to this:

template< typename Arg1 >
bool algorithm( Arg1&& p1, **p2 here** );

With just that its evident that Arg1 will collapse in some way and likely become some kind of reference. p1 is also an output of the algorithm and so if the caller chooses to pass in an lvalue, the function will return with the original parameter modified to reflect where it left off.

However p2 is of the same type but will never be modified. So actually I'd like to put a 'const' promise in there for correctness. Clearly if Arg1 deduces to be a reference, I cannot add const to it.

So my solution is this:

template< class T >
struct make_const_ref
{
typedef typename std::add_reference< typename std::add_const< typename std::remove_reference< T >::type >::type >::type type;
};

template< typename Arg1 >
bool algorithm( Arg1&& p1, typename make_const_ref<Arg1>::type p2 );

Which goes through a bunch of silly machinations to remove some qualifiers and then stick the const& back on.

So my questions are:

1) Is this the best way of doing it?

2) Is there a context under which this will fail? Right now it seems pretty good to me.

1

There are 1 answers

0
Lee Clagett On

I can think of one change that is surprising, but does not result in outright failure. The second parameter is disabled from argument deduction, so the type no longer has to match the first parameter type exactly. This allows for implicit conversions to be accepted, as-if the template argument were specified for the function. An example:

struct Foo
{
    int value;

    operator int() const
    {
        return value;
    }
};

int main()
{
    algorithm(0, Foo{0});
}

whereas the expression:

template<typename Arg>
bool algorithm(const Arg& p1, const Arg2& p2)

will fail to compile with algorithm(0, Foo{0}) unless it was explicitly algorithm<int>(0, Foo{0}). If you were hoping for exact matches only, then this could be problematic. In some cases this desirable; boost::clamp is one example I am aware of that does this intentionally.

Bonus

This is similar to some of the comments to your question, but the trait can be simplified:

template<typename Type>
using const_ref_t = 
    typename std::add_const<
        typename std::add_lvalue_reference<Type>::type>::type;

std::add_lvalue_reference will do what you want in all situations.

Edit

I put this as comment but it should probably go here. This is probably easier:

template<typename Arg1>
bool algorithm(Arg1&& p1, const typename std::remove_reference<Arg1>::type& p2)

All you are trying to do is disable argument deduction for the second parameter, and take it by const-reference. This is doing just that.