【问题标题】:Why doesn't auto_ptr construction work using = syntax为什么 auto_ptr 构造不能使用 = 语法
【发布时间】:2009-04-16 18:32:48
【问题描述】:

我遇到了一个对我来说没有多大意义的编译器错误:

#include <memory>
using namespace std;

auto_ptr<Table> table = db->query("select * from t");

错误:从 'Table*' 转换为非标量类型 'std::auto_ptr

' 请求

但是,以下行确实有效:

auto_ptr<Table> table(db->query("select * from t"));

构造函数的这个定义阻止它按我预期工作的原因是什么?我认为初始化声明使用了构造函数。

这是我的auto_ptr 的构造函数(来自 SGI STL):

explicit
auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) { }

【问题讨论】:

    标签: c++ gcc stl auto-ptr


    【解决方案1】:

    这是“显式”关键字。

    template <typename T>
    struct foo
    {
      explicit foo(T const *)
      {
      }
    };
    
    
    template <typename T>
    struct bar
    {
      bar(T const *)
      {
      }
    };
    
    
    int main(int argc, char **argv)
    {
      int a;
      foo<int> f = &a; // doesn't work
      bar<int> b = &a; // works
    }
    

    “explicit”关键字防止构造函数用于隐式类型转换。考虑以下两个函数原型:

    void baz(foo<int> const &);
    void quux(bar<int> const &);
    

    使用这些定义,尝试使用 int 指针调用这两个函数:

    baz(&a);  // fails
    quux(&a); // succeeds
    

    在 quux 的情况下,您的 int 指针被隐式转换为 bar。

    编辑:要扩展其他人的评论,请考虑以下(相当愚蠢的)代码:

    void bar(std::auto_ptr<int>);
    
    
    int main(int argc, char **argv)
    {
      bar(new int()); // probably what you want.
    
      int a;
      bar(&a); // ouch. auto_ptr would try to delete a at the end of the
               // parameter's scope
    
      int * b = new int();
      bar(b);
      *b = 42; // more subtle version of the above.
    }
    

    【讨论】:

    • 呃。就在我说服自己确实找到了 auto_ptr 的用途时,它的设计让我大吃一惊。返回到旧删除。
    • 不要仅仅为此而放弃 auto_ptr。只需使用显式构造函数: auto_ptr table(db->query("select * from t"));或者移动 shared_ptr。
    • 构造函数显式是有充分理由的。它可以保护您免受意外错误的影响。放置 auto_ptr(...) 很容易。
    • 无论如何,在这种情况下,您不应该使用 equals ... 就好像您依赖编译器为您优化一样。只需认识到 auto_ptr 的 ctor 需要一个 T* 并适当地构造。当您可以拥有 1 个构造时,为什么还要进行构造和复制?
    • 这里没有编译器优化。两种语法总是转换为完全相同的 - 初始化 c'tor 的调用。
    【解决方案2】:

    你需要使用

    auto_ptr<Table> table = auto_ptr<Table>(db->query("select * from t"));
    

    auto_ptr 没有为其模板类型定义赋值运算符。唯一允许的赋值来自另一个 auto_ptr(它的构造函数来自指针是显式的)。这样做是为了防止意外误用 auto_ptr,因为 auto_ptr 假定了内存的所有权。

    我的猜测是,您需要分配表单才能使用多个查询,例如:

    // initialize using constructor
    auto_ptr<Table> table(db->query("select * from t1"));
    ...
    // new query using assignment
    table = auto_ptr<Table>(db->query("select * from t2"));
    ...
    // another query using assignment
    table = auto_ptr<Table>(db->query("select * from t3"));
    

    【讨论】:

    • 为什么不auto_ptr&lt;Table&gt; table(db-&gt;query("select * from t"));
    • 好吧,他自己也想通了(见他的问题)。每当您一个接一个地执行多个查询时,分配表单都是必需的:-)
    • 我希望有一种方法可以编辑 cmets。 uou - 该死的快速打字;-)
    • 你可以删除cmets(然后重新输入,sans-typo)
    • lothar,我也希望进行评论编辑。当我们使用 Markdown 时。 :)
    【解决方案3】:

    构造函数被声明为显式,这意味着它不会用于隐式类型转换。隐式转换为 auto_ptr 很容易导致不良情况,因为 auto_ptr 正在获取指针的所有权。

    例如,如果 auto_ptr 允许从指针进行隐式转换,并且您不小心将指针传递给采用 auto_ptr 的方法,则该指针将被静默转换为 auto_ptr 并随后在函数结束时被删除,即使这不是意向。但是通过将构造函数标记为显式转换不能再静默发生,并且通过调用构造函数,您可以清楚地表达将所有权传递给 auto_ptr 的意图,从而避免任何潜在的混淆。

    void fun(std::auto_ptr<Foo> foo) // Assume implicit conversion is allowed.
    {
        // do stuff with foo
    }
    
    Foo *foo = new Foo();
    
    f(foo); // Normally this isn't allowed.
    
    foo->bar(); // Oops
    

    【讨论】:

      【解决方案4】:

      补充一下lothar 所说的:因为auto_ptr 构造函数是用explicit 关键字声明的,所以您需要使用显式转换从原始指针创建auto_ptr。 (在引入 explicit 之前,隐式强制转换是许多新的和经验丰富的 C++ 开发人员的祸根。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-11
        • 2017-04-07
        • 1970-01-01
        • 2016-02-17
        • 2011-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多