Home > OS >  SFINAE when using lvalue ref but success when using rvalue ref
SFINAE when using lvalue ref but success when using rvalue ref

Time:06-14

I searched but really couldn't find an answer why SFINAE happens only when the argument is passed by lvalue ref, but the build succeeds when the arg is passed by rvalue ref:

template <typename T>
class A {
 public:
  using member_type = T;
};

template <typename AType>
typename AType::member_type f(AType&& m) {
  typename AType::member_type res{};
  return res;
}

void demo() {
  A<int> a;

  // ERROR: candidate template ignored: substitution failure 
  // [with AType = A<int> &]: type 'A<int> &' 
  // cannot be used prior to '::' because it has no members
  f(a); 

  // BUILD SUCCESS
  f(std::move(a)); 
}

CodePudding user response:

When you have

template <typename AType>
typename AType::member_type f(AType&& m)

You have what is called a forwarding reference. Even though it looks like an rvalue reference, this reference type can bind to lvalues or rvalues. The way it works is when you pass an lvalue to f, AType gets deduced to being T&, and when you pass an rvalue AType gets deduced to just T.

So, when you do f(a); AType gets deduced as A<int>&, and you try to form the return type of A<int>&::member_type which is invalid as references do not have type members.

Conversely when you do f(std::move(a));, AType gets deduced to A<int> and A<int> does have a member_type type member.

To fix this you can remove the reference-ness of type by using std::decay_t like

template <typename AType>
auto f(AType&& m) {
  typename std::decay_t<AType>::member_type res{};
  return res;
}
  • Related