Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ Pimpl习语与交换_C++_C++11_Swap_Unique Ptr_Pimpl Idiom - Fatal编程技术网

C++ Pimpl习语与交换

C++ Pimpl习语与交换,c++,c++11,swap,unique-ptr,pimpl-idiom,C++,C++11,Swap,Unique Ptr,Pimpl Idiom,我有几个基于PIMPL习惯用法的类(其中,unique\u ptr指的是实际的实现结构) 我没有添加friendswap函数(如所述),因为据我所知,标准std::swap使用移动语义,可以很好地交换唯一的ptrs。到目前为止,一切顺利 然而,我从Scott Meyers那里读到(有些过时的高效C++),在第25项中说: 但是,默认的交换实现可能不会让您激动,它涉及到复制三个对象:a到temp、b到a和temp到b.[…]对于某些类型,默认交换会使您走上快车道,进入慢车道。在这些类型中,最重要的

我有几个基于PIMPL习惯用法的类(其中,
unique\u ptr
指的是实际的实现结构)

我没有添加
friend
swap
函数(如所述),因为据我所知,标准
std::swap
使用移动语义,可以很好地交换
唯一的ptr
s。到目前为止,一切顺利

然而,我从Scott Meyers那里读到(有些过时的
高效C++
),在第25项中说:

但是,默认的交换实现可能不会让您激动,它涉及到复制三个对象:a到temp、b到a和temp到b.[…]对于某些类型,默认交换会使您走上快车道,进入慢车道。在这些类型中,最重要的是那些主要由指向另一个包含真实数据的类型的指针组成的类型。这种设计的常见表现形式是“pimpl”习惯用法

之后,他还建议专门研究
std::swap

我的问题是,这在C++11中是否仍然有效。似乎C++11
swap
对于pimpl类来说效果很好。我知道添加一个
friend
swap
允许STL使用依赖于参数的查找等,但我更喜欢尽可能精简我的类

我的问题是,这在C++11中是否仍然有效

只是程度要小得多

自从在C++11中引入移动语义以来,通用交换不再复制,而是移动


移动通常更接近于最佳交换实现,因此通常不需要编写自定义实现自定义实现可能会更好。编写自定义实现是否足够快,是否有益,可以通过测量性能来确定。

这里的问题可能是,对于由
std::unique\u ptr
实现的PIMPL,您基本上需要在f头文件(参见Meyers的《有效的现代C++》第22项)。然后,
std::swap
没有“参见”这些定义和编译器无法优化掉不必要的操作,例如不再使用的空指针设置、使用空指针参数调用
运算符delete
等。它只会生成4个
调用
指令,因为它没有其他选项

考虑针对此类的一个示例:

class X
{
    public: 
        X(X&&);
        X& operator=(X&&);
        ~X();

        void swap(X& other) { std::swap(pimpl_, other.pimpl_); }

    private:
        class Impl;
        std::unique_ptr<Impl> pimpl_;
};
使用
X::swap
为同一操作生成的程序集为:

f2(X&, X&):
        mov     rax, QWORD PTR [rdi]
        mov     rdx, QWORD PTR [rsi]
        mov     QWORD PTR [rdi], rdx
        mov     QWORD PTR [rsi], rax
        ret
后者显然是最优的,因为它只涉及交换两个常规指针所需的指令(在本例中隐藏在
std::unique_ptr
后面)


此外,即使对于空的
X::Impl
类,所涉及的特殊成员函数的生成程序集也有许多指令:

X::X(X&&):
        mov     rax, QWORD PTR [rsi]
        mov     QWORD PTR [rdi], rax
        mov     QWORD PTR [rsi], 0
        ret
X::operator=(X&&):
        push    r12
        mov     r12, rdi
        mov     rax, QWORD PTR [rsi]
        mov     QWORD PTR [rsi], 0
        mov     rdi, QWORD PTR [rdi]
        mov     QWORD PTR [r12], rax
        test    rdi, rdi
        je      .L7
        mov     esi, 1
        call    operator delete(void*, unsigned long)
.L7:
        mov     rax, r12
        pop     r12
        ret
X::~X() [base object destructor]:
        mov     rdi, QWORD PTR [rdi]
        test    rdi, rdi
        je      .L12
        mov     esi, 1
        jmp     operator delete(void*, unsigned long)
.L12:
        ret

我认为,在某些情况下,定制实现甚至可以更为优化(例如,请参见我的答案)@DanielLangr相对差异可能很大,但我仍然希望通用交换非常快。它不是非常非常快。这是否会产生差异很大程度上取决于你将花多少时间交换。
X::X(X&&):
        mov     rax, QWORD PTR [rsi]
        mov     QWORD PTR [rdi], rax
        mov     QWORD PTR [rsi], 0
        ret
X::operator=(X&&):
        push    r12
        mov     r12, rdi
        mov     rax, QWORD PTR [rsi]
        mov     QWORD PTR [rsi], 0
        mov     rdi, QWORD PTR [rdi]
        mov     QWORD PTR [r12], rax
        test    rdi, rdi
        je      .L7
        mov     esi, 1
        call    operator delete(void*, unsigned long)
.L7:
        mov     rax, r12
        pop     r12
        ret
X::~X() [base object destructor]:
        mov     rdi, QWORD PTR [rdi]
        test    rdi, rdi
        je      .L12
        mov     esi, 1
        jmp     operator delete(void*, unsigned long)
.L12:
        ret