0

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.

qeadz
  • 1,476
  • 1
  • 9
  • 17
  • 1
    `typedef typename std::add_reference< typename std::add_const< typename std::remove_reference< T >::type >::type >::type type;` Hmmm why not just `using type = typename std::remove_reference::type const&;`? – dyp Jun 04 '14 at 23:13
  • *"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."* Returning it is not an option? Typically, returning is cleaner since it doesn't produce side effects (use a tuple to return more than one value). – dyp Jun 04 '14 at 23:14
  • *"However p2 is of the same type but will never be modified."* Does it *need* to be of the *same* type? You can also use another template parameter plus a `static_assert` or SFINAE check if it has to be the *same* type. – dyp Jun 04 '14 at 23:15
  • @dyp Actually I can use a template alias. Until very recently we were on VS2012 so it simply slipped my mind. Thank it - it makes it neater. – qeadz Jun 04 '14 at 23:27
  • True, but note that type traits and other type functions (metafunctions) typically / per convention are classes (`struct`) with a nested typedef `type`. You can provide an *additional* alias template for convenience, but I'd stick to the convention for traits for better reusability. – dyp Jun 04 '14 at 23:31
  • @dyp Thank you for the suggestions. I've gone with what I have since the template alias you suggested clears up the function prototype to be nicely readable. I've preferred this to the additional template arguments and SFINAE/static_assert options. For some context the arguments are much like iterator pairs except the algorithm cannot complete in one pass. So in essence the mutable arguments are providing data to operate on as well as progress through the data. – qeadz Jun 05 '14 at 00:57

1 Answers1

0

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.

Community
  • 1
  • 1
Lee Clagett
  • 281
  • 1
  • 5