在类方法实现的内部使用 `using std::swap` 是什么意思?

44

我试着学习并采用复制交换惯用法,参考了这个问题中详细的解释:the Copy-Swap Idiom

但我发现了一些我从未见过的代码:在这个例子中出现了using std::swap; // allow ADL

class dumb_array
{
public:
    // ...

    void swap(dumb_array& pOther) // nothrow
    {
        using std::swap; // allow ADL    /* <===== THE LINE I DONT UNDERSTAND */

        swap(mSize, pOther.mSize); // with the internal members swapped,
        swap(mArray, pOther.mArray); // *this and pOther are effectively swapped
    }
};
  1. 函数实现体内出现的 using std::swap; 是什么意思?
  2. ADL 是什么意思?

2
我认为MSalter的回答是更好、更全面的答案,因为他解释了ADL(这也是在此处使用using的整个原因)。我认为你应该选择那个作为解决方案。 - Cheers and hth. - Alf
3个回答

85

这个机制通常用于模板化代码,即template <typename Value> class Foo

现在问题是使用哪种交换方式。 std::swap<Value>可以工作,但可能不是最理想的选择。很有可能类型Value有更好的swap重载,但在哪个命名空间中呢?它几乎肯定不在std::中(因为这是非法的),但很可能在Value的命名空间中。但也有可能不在。

在这种情况下,swap(myValue, anotherValue)将获取到“最佳”的交换方式。参数相关查找将在Value所属的命名空间中找到任何交换方式。否则,using指令会生效,并且将实例化和使用std::swap<Value>

在您的代码中,mSize很可能是一个整型,mArray是一个指针。两者都没有关联的命名空间,std::swap对它们来说99.9%是最优选择。因此,在这里声明using std::swap;似乎是毫无意义的。


7
我认为在std命名空间内提供标准模板函数的特化是合法的。根据§17.4.3.1/1:“程序可以为任何标准库模板添加模板特化到命名空间std中。这样一个标准库模板的特化(完全或部分),除非声明依赖于具有外部链接的用户定义名称,否则会导致未定义行为,并且除非特化符合原始模板的标准库要求。” 话虽如此,在std命名空间中实现 swap 特化并不常见。 - David Rodríguez - dribeas
10
@dribeas 表示,特化 std::swap 是合法的,如果对用户存有疑虑,应该特化 std::swap 并且 在自己的命名空间中提供一个 swap 函数以便通过 ADL 找到。但是,特化 std::swap 有一些限制,其中最致命的限制在于无法进行部分特化,因为它是函数模板,所以如果您编写了一个类模板(比如新容器),则无法为其特化 std::swap,因此必须采用 ADL 方法。MSalters 的说法是正确的:不能重载 std::swap,只能特化。 - Steve Jessop

15

using 关键字具有作用域效应。

这意味着在使用 using 关键字的作用域内,std::swap 可以被引用为 swap


22
在这个上下文中,“ADL” 意味着“参数相关的查找”,这让代码可以在定义了本地 swap 函数或没有定义本地 swap 函数的情况下都能使用“swap”。如果本地函数不存在,则使用 std::swap。 - vmpstr
3
如果您将其作为单独的答案发布,我会投票支持并为您赢得10个声望分数。 - Benoit
6
说实话,这个回答毫无用处。它没有解释using std::swap和ADL的交互作用。MSalters的回答应该被接受。 - Maxim Egorushkin
1
@Maxim Yegorushkin:答案可能不完整,但如果提问者不知道using关键字,那么答案并不是无用的。顺便说一句,我已经为MSalters的答案点赞了,因为它比我的更深入。 - Benoit
1
@Maxim,我的问题主要是关于using关键字,然后只是询问ADL是什么。我很高兴@Benoit和@MSalters分别回答了1.和2。 - Stephane Rolland

-1
通过添加 using std::swap,可以在该函数内提供更广泛的灵活性。由于它是一个模板,所以没有特定类型的交换版本,因此调用任何内置类型的 std 版本都不会产生问题。
假设类 Foo 有一个成员 h,它持有类型 ExampleType,并且我们为 ExampleType 定义了一个特定类型的 swap 版本:
void swap(Foo& lhs, Foo& rhs)
{
    //ERROR: This would only the call std version 
    std::swap(lhs.h, rhs.h);
}

上面的示例只会调用 std 版本,而不是我们的 ExampleType 版本。因此,通过包含 using std::swap,程序将调用我们定义的版本而不是 std::swap
void swap(Foo& lhs, Foo& rhs)
{
    using std::swap;
    std::swap(lhs.h, rhs.h);   //Using class defined version of swap()
}

即使我们用内置函数替换了被交换的参数,代码仍然可以执行而不会出错,因为编译器会推断std版本是最佳选择。这个概念也被称为参数相关查找或ADL。

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