Question

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)

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top