1

Class X contains 2 pieces of data. The templated assignment operator accepts any type and assigns it to member 'd'. However I still want copy assignment to work properly. In MSVC 2010 the line 'b = a;' calls the template assignment operator not the copy assignment. How can I overload assignment to distinguish properly or have the template assignment distinguish internally?

class X
{
public:
    X() : x(0), d(0.0) {}
    X(X const & that) { x = that.x; d = that.d; } 

    X& operator=(X const & that) { x = that.x; d = that.d; }

    template<typename T>
    X& operator=(T && y) {
        //if (T is X&)
        //  operator=(static_cast<X const &>(that));
        //else
        //  d = y;
        return *this;
    }

    int x;
    double d;
};

void f()
{
    X a;
    X b;

    a = 5;
    a = 3.2;
    b = static_cast<X const &>(a);   // calls copy assignment
    b = a;                           // calls template assignment
}
tukra
  • 921
  • 8
  • 13

2 Answers2

3

In MSVC 2010 the line 'b = a;' calls the template assignment operator not the copy assignment

It should call the assignment operator template. We have two viable overloads:

X& operator=(X const &);
X& operator=(X& ); // [T = X&]

And one of the ways of ordering conversion sequences is, from [over.ics.rank]:

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [...] S1 and S2 are reference bindings (8.5.3), 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.

The copy assignment operator references a type that is more cv-qualified than the assignment operator template, so the assignment operator template is preferred. The reason your adding the static_cast forces the compiler to select the copy assignment operator is that now both functions take the exact same argument (X const&), and we simply prefer the function that isn't a function template specialization over the one that is.

The simple way to avoid this is to SFINAE out the operator template so that it doesn't apply to an X or something that derives from X:

template <typename T,
          typename = std::enable_if_t<
              !std::is_base_of<X, std::decay_t<T>>::value
          >>
X& operator=(T&& );

This would make the assignment operator template no longer a viable candidate for b=a;, hence the copy assignment operator becomes trivially the best candidate.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • This definitely seems like the right course but that gives me an "unrecognizable template declaration/definition" error in MSVC 2010. I've never used "typename =" syntax before so it's hard for me to diagnose. – tukra Oct 12 '15 at 19:36
  • 1
    @tukra I don't know how much C++11 support MSVC10 has. You might have to stick that as the return type (`template typename std::enable_if<!std::is_base_of::type>::value, X&>::type operator=(T&&);` ) – Barry Oct 12 '15 at 19:37
  • Awesome!! That works. I have seen a lot of this SFINAE stuff in boost and such and get the gist of its purpose but have never researched its enough to wrap my head around how to actually construct the syntax. I don't recall ever seeing it done on return values though. Please help me understand what's happening. It seems in the case of T=X& the enable_if<>::type does not exist. So apparently a non resolvable element for a return type on a template function is legal and makes the template function disappear? – tukra Oct 12 '15 at 19:52
  • @tukra SFINAE standards for Substitution Failure Is Not An Error. So, if there's a substitution failure - rather than it being a hard error, the overload is removed from consideration. – Barry Oct 12 '15 at 19:58
  • Parsing the acronym is always a good start :) Thanks so much for your help. – tukra Oct 12 '15 at 20:05
-1

Maybe 2 functions instead of a template assignment:

X& operator=(double that) { d = that; return *this; }
X& operator=(int that) { d = that; return *this; }

In C++11 there is also std::is_same, see: How to check for the type of a template parameter?

Community
  • 1
  • 1
ebyrob
  • 667
  • 7
  • 24