4

我试图了解 cast 运算符如何使用模板。

考虑以下代码:

#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 给了我以下编译错误:

请求从“A”转换为非标量类型“S”

我知道,我可以通过添加另一个“operator T() const”来解决问题,但是为什么编译器在这种情况下不能找出正确的转换呢?

奇怪的是,clang 没有抱怨并且编译得很好。

(用gcc4.7、gcc4.8和clang3.2测试过)

4

1 回答 1

6

s2这是因为用 a初始化const S &需要两次转换;一种将引用转换为临时引用,另一种将临时引用复制到您的变量中。C++ 只允许发生一次自动转换。

例如,这也有效:

S s2(a);

由于不再需要创建临时。

请注意,该标准有一段关于这种特殊情况的段落。在 C++03 8.5p14 中:

否则(即,对于剩余的复制初始化情况),可以从源类型转换到目标类型或(当使用转换函数时)到其派生类的用户定义转换序列被枚举,如 13.3 中所述。 1.4,最好的一个是通过重载决议(13.3)选择的。如果转换无法完成或不明确,则初始化格式错误。以初始化表达式作为参数调用所选函数;如果函数是构造函数,则调用初始化目标类型的临时值。然后根据上述规则,调用的结果(对于构造函数的情况是临时的)用于直接初始化作为复制初始化目标的对象。在某些情况下,允许实现通过将中间结果直接构造到正在初始化的对象中来消除这种直接初始化中固有的复制;见 12.2、12.8。

于 2013-09-17T16:07:30.850 回答