3

在编写一个类以充当堆分配对象的包装器时,我遇到了隐式类型转换的问题,可以简化为这个简单的示例。

在下面的代码中,包装类管理一个堆分配的对象并隐式转换为对该对象的引用。这允许包装器对象作为参数传递给函数 write(...),因为发生了隐式转换。

但是,当尝试解析对 operator<<(...) 的调用时,编译器会失败,除非进行了显式转换(使用 MSVC8.0、Intel 9.1 和 gcc 4.2.1 编译器进行检查)。

那么,(1)为什么在这种情况下隐式转换会失败?(2)它可能与依赖于参数的查找有关吗?(3) 如果没有明确的演员表,有什么可以做的吗?

#include <fstream>

template <typename T>
class wrapper
{
    T* t;
  public:
    explicit wrapper(T * const p) : t(p) { }
    ~wrapper() { delete t; }
    operator T & () const { return *t; }
};

void write(std::ostream& os)
{
    os << "(1) Hello, world!\n";
}

int main()
{
    wrapper<std::ostream> file(new std::ofstream("test.txt"));

    write(file);
    static_cast<std::ostream&>( file ) << "(2) Hello, world!\n";
    // file << "(3) This line doesn't compile!\n";
}
4

5 回答 5

2

它失败了,因为您正在尝试解析wrapper<T>不存在的类的运算符。如果你想让它在没有演员表的情况下工作,你可以把这样的东西放在一起:

template<typename X> wrapper<T> &operator <<(X &param) const {
    return t << param;
}

不幸的是,我不知道在编译时解决返回类型的方法。幸运的是,在大多数情况下,它与对象的类型相同,包括在这种情况下带有ostream.

编辑:根据 dash-tom-bang 的建议修改代码。将返回类型更改为wrapper<T> &.

于 2009-02-19T23:11:18.533 回答
1

编译器没有足够的上下文来确定operator&将进行有效转换。所以,是的,我认为这与依赖于参数的查找有关:编译器正在寻找一个operator<<可以接受非const wrapper<std::ostream>作为其第一个参数的参数。

于 2009-02-19T23:07:06.610 回答
1

我认为问题与维护一些编译时约束有关。在您的示例中,编译器首先必须找到所有可能的运算符<<。然后,对于它们中的每一个,它应该尝试您的对象是否可以自动(直接或间接)转换为每个 operator<< 能够接受的任何类型。

这个测试可能非常复杂,我认为这仅限于提供合理的编译时间。

于 2009-02-19T23:12:25.677 回答
1

经过一些测试,一个更简单的示例确定了问题的根源。编译器无法从 to 的隐式转换中推断出下面的T模板参数。f2(const bar<T>&)wrapper<bar<int> >bar<int>&

template <typename T>
class wrapper
{
    T* t;
  public:
    explicit wrapper(T * const p) : t(p) { }
    ~wrapper() { delete t; }
    operator T & () const { return *t; }
};

class foo { };

template <typename T> class bar { };

void f1(const foo& s) { }
template <typename T> void f2(const bar<T>& s) { }
void f3(const bar<int>& s) { }

int main()
{
    wrapper<foo> s1(new foo());
    f1(s1);

    wrapper<bar<int> > s2(new bar<int>());
    //f2(s2); // FAILS
    f2<int>(s2); // OK
    f3(s2);
}

在原始示例中,std::ostream实际上是一个typedef用于模板化的类std::basic_ostream<..>,并且在调用模板化函数时也适用相同的情况operator<<

于 2009-02-20T05:47:58.503 回答
0

检查插入运算符的签名...我认为他们采用非常量 ostream 引用?

经 C++03 标准确认,char* 输出运算符的签名为:

template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);

这确实需要非常量引用。所以你的转换运算符不匹配。

如评论中所述:这是无关紧要的。

标准中有关于应用转换的各种限制......也许这需要隐式转换(您的运算符,并强制转换为基本类型),而最多应该应用一个。

于 2009-02-19T22:58:53.120 回答