3

I want to make a decorator for types that have a certain tag (is_gpio_class) that is implemented differently for rvalues (must make a copy) and lvalues (stores a reference). To avoid having to mention the type I use a funtion template that returns the decorated type.

#include <type_traits>
template< typename T > struct always_false : std::false_type {};

// type with the tag to indicate that it is suitable
struct gpio_class {
   typedef void is_gpio_class;
};

// fallback match gives an error
template< class P, class dummy = void > 
struct invert_x {
    static_assert( always_false< P >::value, "no match" );  
};

// this should match an lvalue
template< class P >
struct invert_x< P, typename P::is_gpio_class > : public gpio_class {
   constexpr invert_x( P & pin ) {}   
};

// 'factory' function that dispatches to the correct specialization
template< typename P >
invert_x< P > invert( P && pin ){ return invert_x< P >( pin ); }

int main(){
    gpio_class pin4;   

    // this works
    auto led0 = invert_x< gpio_class>( pin4 );

    // but this does not match the specialization
    auto led1 = invert( pin4 );
}

This works when the function has a & parameter, but to distinguish between rval and lval I think I must pas it as &&, and then somehow match differently for lvalue and rvalue, but how?

  • 1
    did you mean [this](http://coliru.stacked-crooked.com/a/aa227e95359cbd0e) ? – Piotr Skotnicki Feb 14 '15 at 09:52
  • Your template is ill-formed, no diagnostic required. – Columbo Feb 14 '15 at 10:09
  • @Columbo some explanation would be appreciated. – Wouter van Ooijen Feb 14 '15 at 10:21
  • @WoutervanOoijen [temp.res]/8 *"If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic required."* Admittedly, you do instantiate it in your example. The point is that the program cannot be well-formed because either you instantiate that static_assertion or you don't, in which case the above quote steps in. – Columbo Feb 14 '15 at 10:24
  • @Piotr: yes, that seems to do what I want. Make it an answer? – Wouter van Ooijen Feb 14 '15 at 10:49
  • @Columbo: If the fallback is instatiated this it is meant to generate a static_assert because invert() is called with a type that does not have the appropriate tag. This is intentional. My question was how I can write templates that match (dfferently) for an rvalue and an lvalue (answered by Piotr). If my code already did this I wouldn't have asked. – Wouter van Ooijen Feb 14 '15 at 10:55
  • @WoutervanOoijen .. you missed the point. If the fallback isn't instantiated your code is ill-formed. See http://stackoverflow.com/a/27868077/3647361 – Columbo Feb 14 '15 at 10:57
  • @Piotr : I changed the code, better now? – Wouter van Ooijen Feb 14 '15 at 14:39
  • Are you looking for [ref-qualifiers](https://akrzemi1.wordpress.com/2014/06/02/ref-qualifiers/)? – nonsensation Feb 14 '15 at 14:58

1 Answers1

2

You simply need to remove reference-ness from the deduced P in the call to invert_x from invert. Use std::remove_reference_t:

template< typename P >
invert_x< P > invert( P && pin )
{
 return invert_x< std::remove_reference_t<P> >( pin );
}

EDIT : To support rvalue and lvalue overloads, use tag-dispatching like this:

template< typename P >
invert_x< P > invert( P && pin )
{
 return invert_x< std::remove_reference_t<P> >( pin, 
                                                std::is_lvalue_reference<P&&>());
}

And provide these overloads for the constructor of invert_x:

constexpr invert_x( P & pin, std::true_type/*This is the lvalue overload*/ ) {} 
constexpr invert_x( P & pin, std::false_type/*This is the rvalue overload*/ ) {} 
Pradhan
  • 16,391
  • 3
  • 44
  • 59
  • Thanks, that is step 1 (It's a tiny bit more verbose in C+11, but that's no problem), but now I want to have 2 different specializations depending on whether what I pass to invert() is an rvlaue or an lvalue. How can I do that? – Wouter van Ooijen Feb 14 '15 at 09:42
  • 1
    Thanks, but I don't want different constructors: for the lval case, the object must contain (only) a reference, for the rvalue case a full copy of the argument. Hence I want different specializations, as Piotr shows in his linked code, PS the two constructors you show seem 100% identical? – Wouter van Ooijen Feb 14 '15 at 22:34