4

I am trying to understand how the cast operator works with templates.

Consider the following code:

#include <iostream>

using namespace std;

struct S {
    int v;
};

class A {
public:
    A(void* ptr) : ptr(ptr) {}  
    void* ptr;

    template<typename T>
    const T& as() const {
        return *static_cast<T*>(ptr);
    }

    template<typename T>
    operator const T&() const {
        return as<T>();
    }
};

int main() {
    S test;
    test.v = 123;

    A a(&test);

    S s = a.as<S>();
    S s2 = a; // error here
    const S& s3 = a;

    cout << s.v << endl;
    cout << s2.v << endl;
    cout << s3.v << endl;

    return 0;
}

gcc gives me the following compile error:

conversion from ‘A’ to non-scalar type ‘S’ requested

I am aware, that I can fix the problem by adding another "operator T() const" but why can't the compiler figure out the right conversion in this case?

Strangely clang does not complain and compiles just fine.

(tested with gcc4.7, gcc4.8 and clang3.2)

user1492625
  • 313
  • 5
  • 10

1 Answers1

6

This is because initializing s2 with a const S & requires two conversions; one to convert the reference into a temporary, and one to copy the temporary into your variable. C++ only allows one automatic conversion to happen.

For example, this also works:

S s2(a);

Since there is no longer a need to create the temporary.

Note that the standard has a paragraph on this particular case. In C++03 8.5p14:

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the destination type. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • Ok, I think I understand. I just figured that S s2; s2 = a; also compiles fine. Reading question http://stackoverflow.com/questions/2462773/c-copy-construct-construct-and-assign-question helped too but also shows significant differences between compilers. For example the code from that question fails on both gcc and clang. Does anybody know what the standard says and allows when using the "construction via assignment" syntax in particular? – user1492625 Sep 17 '13 at 16:57
  • @user1492625: This is called copy-initialization. There is a paragraph on this particular case. I'll add it to my answer. – Vaughn Cato Sep 17 '13 at 19:20