小编Dun*_*ndo的帖子

pimpl 成语是否比始终使用 unique_ptr 作为成员变量更好?

在我的工作场所,我们有这样的约定:几乎每个类(除了极少数例外)都使用unique_ptrs、原始指针或引用作为成员变量来实现。

这是因为编译时间的原因:通过这种方式,您只需要在头文件中对类进行前向声明,并且只需要在 cpp 中包含该文件。此外,如果您更改包含的类的 .h 或 .cpp,unique_ptr则不需要重新编译。

我认为这种模式至少有以下缺点:

  • 它迫使您编写自己的复制构造函数和赋值运算符,并且您必须单独管理每个变量,如果您想保持复制的语义。
  • 代码的语法变得非常繁琐,例如您将拥有std::vector<std::unique_ptr<MyClass>>而不是更简单的std::vector<MyPimplClass>.
  • 指针的常量性不会传播到指向的对象,除非你使用 std::experimental::propagate_const,我不能使用它。

因此,我想到建议对作为指针包含的类使用 pImpl 习惯用法,而不是在任何地方使用指针。通过这种方式,我认为我们可以两全其美:

  • 更快的编译时间:pimpl 减少编译依赖
  • 要编写复制构造函数和复制赋值运算符,您只需执行以下操作:
A::A(const A& rhs) : pImpl(std::make_unique<Impl>(*rhs.pImpl)) {}
A& A::operator=(const A& rhs) {
  *pImpl = *rhs.pImpl;
  return *this;
}
Run Code Online (Sandbox Code Playgroud)
  • 常量被传播到成员对象。

在这一点上,我与我的同事进行了讨论,他们认为 pImpl 并不比在任何地方使用指针更好,原因如下:

  • 与使用指针相比,它减少了编译依赖性,因为如果您使用 pImpl,则在更改公共接口时必须重新编译包含 pImpl 类的类:如果您只使用指针而不是 pImpl 类,则不需要即使在更改头文件时也要重新编译。

现在我有点困惑。我认为我们的实际约定并不比 pImpl 好,但我无法争论为什么。

所以我有一些问题:

  • 在这种情况下, pImpl 成语是一个很好的选择吗?
  • 除了我提到的那些之外,我们正在使用的模式还有其他缺点吗?

编辑:我正在添加一些示例来阐明这两种方法。

  • 随着接近unique_ptr为成员:
// B.h
#pragma once
class B {
    int i = 42;
public:
    void print();
}; …
Run Code Online (Sandbox Code Playgroud)

c++ compile-time pimpl

9
推荐指数
1
解决办法
160
查看次数

为什么我需要移动 `std::unique_ptr`

鉴于以下代码:

#include <iostream>
#include <memory>

struct A {};

struct B : public A {};

std::pair<bool, std::unique_ptr<B>> GetBoolAndB() {
    return { true, std::make_unique<B>() };
}

std::unique_ptr<A> GetA1() {
    auto[a, b] = GetBoolAndB();
    return b;
}

std::unique_ptr<A> GetA2() {
    auto [a, b] = GetBoolAndB();
    return std::move(b);
}
Run Code Online (Sandbox Code Playgroud)

GetA1 不编译,出现此错误:

C2440: 'return': cannot convert from 'std::unique_ptr<B,std::default_delete<_Ty>>' to 'std::unique_ptr<A,std::default_delete<_Ty>>'

虽然GetA2编译没有错误。

我不明白为什么我需要调用std::move才能使函数工作。

编辑

只是为了澄清,正如 DanielLangr 在评论中指出的那样,我怀疑的是

std::unique_ptr<A> GetA3() {
    std::unique_ptr<B> b2; 
    return b2;
}
Run Code Online (Sandbox Code Playgroud)

无需std::move.

现在我明白,在GetA1and 的情况下 …

c++ unique-ptr move-semantics c++17 structured-bindings

8
推荐指数
1
解决办法
279
查看次数

filesystem::operator/ boost 和 std 中的不同行为

我正在尝试从 移植boost::filesystemstd::filesystem。在这个过程中,我遇到了一些代码,其中booststd似乎以不同的方式表现。

以下代码显示了该行为:

#include <iostream>
#include <filesystem>
#include <boost/filesystem.hpp>

template<typename T>
void TestOperatorSlash()
{
    const std::string foo = "Foo";
    const std::string bar = "Bar";
    const T start = "\\";
    std::string sep;
    sep += T::preferred_separator;
    const auto output = start / (foo + bar) / sep;
    std::cout << output << '\n';
}

int main(int argc, char** argv)
{
    TestOperatorSlash<std::filesystem::path>();
    TestOperatorSlash<boost::filesystem::path>();
}
Run Code Online (Sandbox Code Playgroud)

该代码给出输出:

"\\"
"\FooBar\"
Run Code Online (Sandbox Code Playgroud)

我的预期行为是boost,我不明白 会发生什么std::filesystem

为什么我得到"\\"?为什么和 …

c++ filesystems boost std

6
推荐指数
1
解决办法
410
查看次数

为什么这个包含顺序会导致 unordered_map 上的链接错误?

我遇到了无法解释的包含顺序问题。我将向您展示一个包含四个文件的最小示例:

// A.h
#pragma once
#include <functional>

struct A {};

namespace std {
    template<>
    class hash<A> {
    public:
        size_t operator()(const A&) const {
            return 0;
        };
    };
}

// B.h
#pragma once
#include <unordered_map>

struct A;

struct B {
    const std::unordered_map<A, int>& GetMap() const;
};

// B.cpp
#include "B.h"
#include "A.h"

const std::unordered_map<A, int>& B::GetMap() const {
    static std::unordered_map<A, int> m;
    return m;
}

// main.cpp
#include "A.h" // To be included AFTER B.h
#include "B.h"

int main() {
    B …
Run Code Online (Sandbox Code Playgroud)

c++ include cl c++17

5
推荐指数
1
解决办法
709
查看次数

虚拟继承和类函数指针大小

我有以下代码,我正在使用 MSVC:

template <typename T>
void CanMock() {
  class SimpleType {};
  static_assert(sizeof(void(SimpleType::*)()) == sizeof(void(T::*)()),
                "Can't mock a type with multiple inheritance or with "
                "non-polymorphic base class");
}

int main() {
  class base {};
  class derived : public base {};
  class derivedVirtual : virtual public base {};
  CanMock<derived>();
  CanMock<derivedVirtual>();
}
Run Code Online (Sandbox Code Playgroud)

in调用时通过,但调用时static_assert失败。CanMockderivedderivedVirtual

据我了解,断言尝试将 a 的类函数指针的大小SimpleType与 的类函数指针的大小进行比较derivedVirtual

现在我的问题是:

  • 我知道 a 的大小derivedVirtual应该增加,因为其中存储了 vtable 指针(对吗?),但是为什么derivedVirtual继承 with 时类函数指针的大小会增加virtual
  • 为什么在 clang …

c++ virtual inheritance mocking fakeit

5
推荐指数
0
解决办法
125
查看次数