无法手动调用std::string的析构函数

6

我正在尝试使用联合体,并创建了一个带有匿名联合体成员的示例类A。由于联合体包含一个std::string和一个std::vector,因此我需要为该类定义析构函数。但是,当我尝试手动调用~string()时出现问题。

union.cpp: In destructor 'A::~A()':
union.cpp:14:14: error: expected class-name before '(' token
    s_.~string();

我不理解这个向量的问题。如果我删除对 s._~string(); 的调用,它就可以编译通过。这是编译器的错误吗?我正在使用MinGW64。

#include <string>
#include <vector>

class A
{
    public:
    explicit A(const std::string &s) : s_ {s}, is_string {true} {}
    explicit A(const std::vector<int> &v) : v_ {v}, is_string {false} {}

    ~A()
    {
        if (is_string)
            s_.~string();
        else
            v_.~vector();
    }

    private:
    union
    {
        std::string s_;
        std::vector<int> v_;
    };
    bool is_string;

};

int main()
{
    A a("Hello, World!");
    return 0;
}

使用std::delete_at(std::addressof(s_))-std=c++17标准下是有效的。这是怎么回事?


这个类被称为 std::string,而不仅仅是 string - UnholySheep
1
@UnholySheep std::string似乎无法工作:http://coliru.stacked-crooked.com/a/fd0d78d2219c168a - HolyBlackCat
1
在99.9%的情况下(或大致如此),手动调用析构函数都是一个bug。极少数情况涉及到“placement new”,但这不是你现在所做的。不要手动调用析构函数。当对象超出范围或包含对象被销毁时,它们将会被自动调用。是的,你可以手动调用它们,但你不应该这样做。如果你认为你需要这样做,那么你正在做错事。 - Jesper Juhl
2
@JesperJuhl 手动调用析构函数在实现类似于 std::variant 的东西时是必要的,这正是 OP 在此处所做的。当然,使用 std::variant 本身会更好。 - interjay
@interjay 哦,我不知道 std::variant。那是 C++17 中 union 的替代品吗? - dav
显示剩余5条评论
2个回答

6

std::string 不是真正的类型,而是一个typedef。

using std::string = std::basic_string<char>;

所以你需要调用basic_string的析构函数。

~A()
{
    if (is_string)
        s_.~basic_string();
    else
        v_.~vector();
}

1
标准难道没有明确给出通过typedef手动调用析构函数的示例吗? - John Gowers
@JohnGowers:正确的,参见 https://dev59.com/UFQK5IYBdhLWcg3wNtTA。 - MSalters
在这种情况下,为什么需要这个答案呢?这难道不意味着s_.~string()应该可以工作,即使它实际上不工作吗?事实上,Scott Meyers的《更有效的C++》第41页也声称~string()应该可以工作...???? - xdavidliu
这个答案其实是错误的!它声称你不能通过typedef调用析构函数,但如果我做 typedef std::string Foo 然后 s_.~Foo() 就可以完美地工作(我使用的是macOS Apple clang版本13.1.6和-std=c++98)。 - xdavidliu

2

我刚遇到了同样的问题,以下方法对我有效:

typedef std::string string_alias;
sp->~string_alias();

似乎C++编译器被语句中的~std::string部分搞糊涂了。

编译器有充分的理由感到困惑。你正在销毁一个字符串,而不是整个命名空间。~应该放在std::之后,所以是std::~string - MSalters
gcc 10.3.0 报错:error: scope ‘std’ before ‘~’ is not a class-name,针对 sp->std::~string();。如果没有 typedef,应该如何编写? - hochl
这个答案实际上比被接受的答案更正确,尽管仍然不确定为什么C++编译器会对std中string到basic_string<char>的typedef感到困惑。我真的很想知道答案... - xdavidliu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接