【问题标题】:Unable to find operator via implicit conversion in C++无法通过 C++ 中的隐式转换找到运算符
【发布时间】:2009-02-19 22:53:15
【问题描述】:

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

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

但是,当尝试解析对 operator

那么,(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";
}

【问题讨论】:

  • 我完全错过了包装器的价值:在堆栈上创建的 fstream 对象会自行清理。相反,您在堆上创建了一个 fstream 对象,然后使用包装器来保证它在与开始清理时完全相同的位置被清理。
  • 在实际版本中,我使用 boost::shared_ptr 而不是 T* 以便共享实例,并为共享实例实现必要的语义。这个例子被大大简化了,不是很现实,但足以说明问题。
  • 如果模板的参数是自动解析的,则转换集是有限的。所以在你的情况下,如果你有 template void write(basic_ostream&);你会遇到同样的问题(除了 op

标签: c++ type-conversion operator-keyword implicit-conversion


【解决方案1】:

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

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

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

编辑: 根据 dash-tom-bang 的建议修改了代码。将返回类型更改为wrapper&lt;T&gt; &amp;

【讨论】:

  • > 在编译时解析返回类型 这将需要更高级的模板元编程...加上(可能)类型跟踪和 SFINAE。
  • 这可能是一个很好的解决方法,但是即使没有为 wrapper 定义 operator+=,以下方法仍然有效: wrapper i(new int(10));我 += 1; // 给出 11
  • int case 可能会起作用,因为它是一个整体类型,整个类型的提升/转换更容易发生?例如。如果我 +='ing 一个 int,也许它也试图将 lhs 转换为一个 int?我喜欢这个回应,这可能是要走的路,尽管我可能会返回 wrapper 而不是
  • 好叫破折号。我知道:)
  • 你的 op 破坏了。流的 op & ,它会咬人。你可能想做 *t
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    我认为问题与维护一些编译时约束有关。在您的示例中,编译器首先必须找到 all 可能的运算符

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

    【讨论】:

      【解决方案4】:

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

      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 实际上是模板化类std::basic_ostream&lt;..&gt;typedef,调用模板化函数operator&lt;&lt; 时也是如此。

      【讨论】:

        【解决方案5】:

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

        已确认符合 C++03 标准,char* 输出操作符的签名为:

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

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

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

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

        【讨论】:

        • 谢谢,但这没什么区别。转换运算符是一个 const 成员函数,因为它不会更改指针 t,但转换仍然是 T&,而不是 const T&。这就像 'const T*' 和 'T* const' 之间的区别。
        • 怪最近C++不够用。
        猜你喜欢
        • 1970-01-01
        • 2010-10-27
        • 2013-08-25
        • 1970-01-01
        • 1970-01-01
        • 2010-12-09
        • 2012-05-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多