camino
camino

Reputation: 10584

SFINAE is not applied to auto generated special member function?

class Myclass{
private:
    int i;
public:
    Myclass(){}
    Myclass(const Myclass &lvalue){} //<---
    template<typename T>Myclass(T& lvalue):i(lvalue){}
};

int main()
{
    Myclass a;
    Myclass b(a);
    return 0;
}

The code above fails to compile with:

error: cannot convert ‘Myclass’ to ‘int’ in initialization

Is this a bug? I have tested it using g++ 5.3 and clang3.9

Upvotes: 2

Views: 61

Answers (2)

user6362627
user6362627

Reputation: 115

The other answers are good, but I thought I'd add standard references to complement. The latest draft, section Ranking implicit conversion sequences, states:

  • Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if

    • S1 and S2 are reference bindings ([dcl.init.ref]), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers. [ Example:

      int f(const int &);
      int f(int &);
      int g(const int &);
      int g(int);
      
      int i;
      int j = f(i);                   // calls f(int &)
      

Upvotes: 3

Barry
Barry

Reputation: 303097

Nope, not a bug. And this has nothing to do with SFINAE.

Let's do overload resolution on:

Myclass b(a);

We have two viable overloads:

 Myclass(Myclass const&); // your copy ctor
 Myclass(Myclass& );      // your ctor template, with [T=Myclass]

Both are exact matches. One of the tiebreakers in picking the best viable candidate is to select the least cv-qualified reference - which in this case is the template. This ends up trying to initialize your int with a Myclass, hence the error. (Note, there is a tiebreaker to prefer non-templates to templates - but it's a lower ranked tiebreaker than the cv-qualification on the reference).

In this case, the solution would be to introduce SFINAE to disable this constructor in the case that it should use the copy ctor. That is:

template <class T, class = std::enable_if_t<!std::is_convertible<T*, Myclass const*>::value>>
Myclass(T& );

And now this constructor won't be viable for Myclass b(a).

Upvotes: 4

Related Questions