1

In the following code:

class Class {
};

class Subclass : public Class {
};

template<typename T>
class Template1 {
public:
};

template<typename T>
class Template2 {
public:
    template<typename Y>
    Template2(const Template1<Y>& t1)
    {
    }
};

Template2<Class> f1(Template2<Class>& t2) {
    return Template1<Subclass>();
}

Template2<Class> f2(Template2<Class> t2) {
    return Template1<Subclass>();
}

int main() {
    f1(Template1<Subclass>()); // error C2664: 'Template2<Class> f1(Template2<Class> &)': cannot convert argument 1 from 'Template1<Subclass>' to 'Template2<Class> &'
    f2(Template1<Subclass>()); // OK
    f1(Template2<Class>(Template1<Subclass>())); // OK
}

If I remove the reference from the function or do the conversion myself everything's fine, but the compiler doesn't seem to want to convert from Template1 to Template2& on its own. Why?

Christophe
  • 68,716
  • 7
  • 72
  • 138
Jorge Rodriguez
  • 603
  • 1
  • 6
  • 14
  • It's going to be very confusing trying to explain what's going on here using generic labels like 'Class', which is identical to the keyword `class` except for its leading uppercase character. You'll be better off rewriting the question and using more generic, completely unrelated labels, like `class Fruit`, `class Vegetable`, and so on. Same goes for template names. Any resulting explanation will be much easier to follow, without having to use, basically, the same words over and over again differing only in their capitalization. – Sam Varshavchik Mar 02 '19 at 19:37
  • 1
    Use `/permissive-`. That should make things a bit clearer, since the third call should no longer compile either :-). That leaves you with "why can't I bind a non-const lvalue reference to a temporary". If your version of MSVC is too old and doesn't accept that option, do everything in your power to update to a more recent version. – bogdan Mar 02 '19 at 19:47

1 Answers1

1

The problem

Your f1() function needs an lvalue for the argument passed by reference. Unfortunately, the implicit conversion produces a temporary value that cannot be used as an lvalue. This causes the C2664 error message.

The solution

You just need to avoid trying to refer to a temporary value resulting from the conversion, by defining your function as

Template2<Class> f1(const Template2<Class>& t2) {  // const & can be used for temp
    return Template1<Subclass>();
}

or, better, as

Template2<Class> f1(Template2<Class>&& t2) {    // && is meant for exactly such cases
    return Template1<Subclass>();
}

You can even have two such constructors, one for lvalue references & and one for rvalue references &&, if it could be important to make the difference. Here an online demo

Christophe
  • 68,716
  • 7
  • 72
  • 138