草庐IT

c++ - 公众好友交换成员(member)功能

coder 2023-04-24 原文

在美丽的回答copy-and-swap-idiom有一段代码我需要一些帮助:

class dumb_array
{
public:
    // ...
    friend void swap(dumb_array& first, dumb_array& second) // nothrow
    {
        using std::swap; 
        swap(first.mSize, second.mSize); 
        swap(first.mArray, second.mArray);
    }
    // ...
};

他添加了一个注释

There are other claims that we should specialize std::swap for our type, provide an in-class swap along-side a free-function swap, etc. But this is all unnecessary: any proper use of swap will be through an unqualified call, and our function will be found through ADL. One function will do.



friend我必须承认,我有点“不友好”。所以,我的主要问题是:
  • 看起来像是免费功能 ,但它在类体内?
  • 为什么不是这个 swap静态 ?它显然不使用任何成员变量。
  • “任何正确使用交换都会通过 ADL 找到交换” ? ADL 会搜索命名空间,对吧?但它也在类内部查看吗?或者是这里friend进来?

  • 附带问题:
  • 使用 C++11,我应该标记我的 swap s 与 noexcept ?
  • 使用 C++11 及其 范围为 , 我应该放置 friend iter begin()friend iter end()类里面的方法一样吗?我认为friend这里不需要吧?
  • 最佳答案

    有几种写法swap ,有些比其他的好。然而,随着时间的推移,人们发现单一的定义效果最好。让我们考虑如何考虑编写 swap功能。

    我们首先看到容器如 std::vector<>有一个单参数成员函数 swap , 如:

    struct vector
    {
        void swap(vector&) { /* swap members */ }
    };
    

    那么,我们类自然也应该这样吧?嗯,不是真的。标准库有 all sorts of unnecessary things , 和成员(member) swap是其中之一。为什么?我们继续。

    我们应该做的是确定什么是规范的,以及我们的类需要做什么来处理它。交换的规范方法是 std::swap .这就是成员函数没有用的原因:它们不是我们应该交换的东西,一般来说,并且与 std::swap 的行为无关。 .

    那么,制作 std::swap我们应该提供的工作(和 std::vector<> 应该提供)std::swap 的特化, 对?
    namespace std
    {
        template <> // important! specialization in std is OK, overloading is UB
        void swap(myclass&, myclass&)
        {
            // swap
        }
    }
    

    嗯,在这种情况下肯定会起作用,但它有一个明显的问题:函数特化不能是部分的。也就是说,我们不能用这个专门化模板类,只有特定的实例化:
    namespace std
    {
        template <typename T>
        void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization
        {
            // swap
        }
    }
    

    这种方法在某些时候有效,但不是所有时间。一定会有更好的办法。

    有!我们可以使用 friend功能,并通过ADL找到它:
    namespace xyz
    {
        struct myclass
        {
            friend void swap(myclass&, myclass&);
        };
    }
    

    当我们想交换某些东西时,我们会联想† std::swap然后调用一个不合格的电话:
    using std::swap; // allow use of std::swap...
    swap(x, y); // ...but select overloads, first
    
    // that is, if swap(x, y) finds a better match, via ADL, it
    // will use that instead; otherwise it falls back to std::swap
    

    什么是friend功能?该地区存在困惑。

    在 C++ 标准化之前,friend函数执行了称为“ friend 名称注入(inject)”的操作,其中代码的行为就好像函数已写入周围的命名空间中一样。例如,这些是等效的预标准:
    struct foo
    {
        friend void bar()
        {
            // baz
        }
    };
    
    // turned into, pre-standard:    
    
    struct foo
    {
        friend void bar();
    };
    
    void bar()
    {
        // baz
    }
    

    然而,当 ADL被发明了这个被删除了。 friend然后只能通过 ADL 找到函数;如果您希望将其作为自由函数,则需要将其声明为(例如 see this )。但是!有问题。

    如果您只是使用 std::swap(x, y) ,您的重载将永远不会被发现,因为您已经明确说“在 std 中查找,而不是其他任何地方”!这就是为什么有些人建议编写两个函数:一个是通过 ADL 找到的函数。 ,另一个处理显式 std::资格。

    但正如我们所见,这不能在所有情况下都有效,我们最终会弄得一团糟。相反,惯用的交换走另一条路:而不是让类的工作提供std::swap ,这是交换者的工作,以确保他们不使用合格的 swap ,如上。只要人们知道它,这往往效果很好。但问题就在这里:需要使用不合格的调用是不直观的!

    为了使这更容易,像 Boost 这样的一些库提供了函数 boost::swap ,它只是对 swap 进行不合格的调用, 与 std::swap作为关联的命名空间。这有助于使事情再次简洁,但它仍然令人失望。

    请注意,C++11 中 std::swap 的行为没有变化。 ,我和其他人错误地认为会是这种情况。如果您对此有所了解,read here .

    简而言之:成员函数只是噪音,特化丑陋且不完整,但friend功能齐全,工作正常。当你交换时,要么使用 boost::swap或不合格swapstd::swap联系。

    †非正式地,如果在函数调用期间会考虑某个名称,则该名称是相关联的。有关详细信息,请阅读第 3.4.2 节。在这种情况下,std::swap通常不考虑;但是我们可以将它关联起来(将它添加到未限定的 swap 考虑的重载集合中),从而可以找到它。

    关于c++ - 公众好友交换成员(member)功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5695548/

    有关c++ - 公众好友交换成员(member)功能的更多相关文章

    1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    2. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

      对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

    3. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

      只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

    4. ruby - 使用 `+=` 和 `send` 方法 - 2

      如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

    5. ruby - 如何计算 Liquid 中的变量 +1 - 2

      我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

    6. arrays - Ruby 数组 += vs 推送 - 2

      我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

    7. ruby-on-rails - rails 功能测试 - 2

      在Rails自动生成的功能测试(test/functional/products_controller_test.rb)中,我看到以下代码:classProductsControllerTest我的问题是:方法调用products()在哪里/如何定义?products(:one)到底是什么意思?看代码,大概意思是“创建一个产品”,但是它是如何工作的呢?注意我是Ruby/Rails的新手,如果这些是微不足道的问题,我深表歉意。 最佳答案 如果您查看test/fixtures文件夹,您会看到一个products.yml文件。这是在您创建

    8. += 的 Ruby 方法 - 2

      有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

    9. ruby - Sinatra + Heroku + Datamapper 使用 dm-sqlite-adapter 部署问题 - 2

      出于某种原因,heroku尝试要求dm-sqlite-adapter,即使它应该在这里使用Postgres。请注意,这发生在我打开任何URL时-而不是在gitpush本身期间。我构建了一个默认的Facebook应用程序。gem文件:source:gemcuttergem"foreman"gem"sinatra"gem"mogli"gem"json"gem"httparty"gem"thin"gem"data_mapper"gem"heroku"group:productiondogem"pg"gem"dm-postgres-adapter"endgroup:development,:t

    10. ruby-on-rails - 功能测试 Authlogic? - 2

      在我的一些Controller中,我有一个before_filter检查用户是否登录?用于CRUD操作。application.rbdeflogged_in?unlesscurrent_userredirect_toroot_pathendendprivatedefcurrent_user_sessionreturn@current_user_sessionifdefined?(@current_user_session)@current_user_session=UserSession.findenddefcurrent_userreturn@current_userifdefine

    随机推荐