c ++中的扩展方法

Bob*_*Bob 54 c++ c++11

我在c ++中搜索扩展方法的实现,并且在这个comp.std.c ++讨论中提到了polymorphic_map可以用于与类关联的方法,但是,提供的链接似乎已经死了.有没有人知道答案所指的是什么,或者是否有另一种方法以类似的方式扩展类到扩展方法(可能通过一些mixins的使用?).

我知道规范的C++解决方案是使用自由函数; 这更多是出于好奇而不是其他任何事情.

Dav*_*eas 68

不同语言以不同方式处理发展.特别是对于OO而言,C#和Java有一个强烈的观点,导致一切都是一个对象心态(C#在这里稍微宽松一点).在该方法中,扩展方法提供了一种扩展现有对象或接口以添加新功能的简单方法.

C++中没有扩展方法,也不需要它们.在开发C++时,忘记一切都是对象范例 - 顺便说一下,即使在Java/C#[*]中也是如此.在C++中采用了不同的思维方式,有对象,并且对象具有本身就是对象的一部分的操作,但是还有其他操作构成了接口的一部分,不需要成为类的一部分.Herb Sutter必须阅读的内容是什么?,作者辩护(并且我同意)您可以使用简单的自由函数轻松扩展任何给定的类.

作为一个特别简单的示例,标准模板化类basic_ostream具有一些成员方法来转储某些基本类型的内容,然后通过使用现有公共接口将该功能扩展到其他类型的(也是模板化的)自由函数进行增强.例如,std::cout << 1;实现为成员函数,std::cout << "Hi";而是根据其他更基本的成员实现的自由函数.

C++中的可扩展性是通过自由函数实现的,而不是通过向现有对象添加新方法的方式实现的.

[*]一切都不是对象.

在给定的域中,将包含一组可以建模的实际对象以及可以应用于它们的操作,在某些情况下,这些操作将是对象的一部分,但在某些其他情况下它们不会.特别是你会发现语言中的实用程序类声称一切都是对象,而那些实用程序类只不过是一个试图掩盖这些方法不属于任何特定对象的事实的层.

甚至一些作为成员函数实现的操作也不是对象的真正操作.考虑添加一个Complex数字类,第一个参数的操作sum(或+)多于第二个参数?为什么a.sum(b);b.sum(a)不应该sum( a, b )

强制操作成为成员方法实际上产生了奇怪的效果 - 但我们只是习惯了它们:即使实现完全对称a.equals(b);,b.equals(a);也可能有完全不同的结果equals.(考虑当其中一个a或是b空指针时会发生什么)

  • **C++中没有扩展方法,也不需要它们.**.那是你的意见.我认为每种语言都应该提供语法糖. (58认同)
  • 我真的很喜欢这个例子. (7认同)
  • 你对D的统一函数调用语法有什么看法?因此,代替IIUC为成员函数提供自由函数语法的扩展方法,D反过来为自由函数提供成员函数语法.所以在名称查找期间,如果`obj.fun()`没有找到成员`Obj :: fun()`,它继续寻找一个自由函数`fun(Obj const&)`.这实现了函数调用的自然"中缀"链接.参见例如http://www.drdobbs.com/architecture-and-design/component-programming-in-d/240008321?pgno=4您是否也会遇到您提及的转换问题? (4认同)
  • @Krythic:什么是真正的*面向对象的语言?当我看到声称某些非常成功的语言或产品充满*糟糕的设计选择*时,我不得不微笑.有两种类型的编程语言,人们抱怨和人们不使用的语言. (4认同)
  • 扩展方法实际上非常方便。自由函数需要程序员已经知道函数的存在。如果存在扩展方法,则只需要“加点”对象,并在建议中看到它即可。有时使用快捷方式ext方法修补第3方库将确实有助于使事情变得顺利。当然,您也可以将自由函数包含在名称空间中,以享受“点”的建议,但这只会使代码行更长。 (2认同)
  • 在某些情况下,扩展方法(通常可以使用方法和属性来扩展类)非常方便。一个很好的例子是由某些工具(例如[Microsoft Bond](https://github.com/Microsoft/bond)或[Google Protocol Buffers](https://developers.google.com/protocol-buffers/ )。具有扩展名允许使用必要的处理和操作方法以及可能无法保留但可用于计算和表示目的的其他属性来扩展这些类。 (2认同)

Aki*_*shi 24

Boost Range Library的方法使用operator |().

r | filtered(p);
Run Code Online (Sandbox Code Playgroud)

我也可以用同样的方式为字符串编写trim.

#include <string>

namespace string_extension {

struct trim_t {
    std::string operator()(const std::string& s) const
    {
        ...
        return s;
    }
};

const trim_t trim = {};

std::string operator|(const std::string& s, trim_t f)
{
    return f(s);
}

} // namespace string_extension

int main()
{
    const std::string s = "  abc  ";

    const std::string result = s | string_extension::trim;
}
Run Code Online (Sandbox Code Playgroud)


Bal*_*arq 8

简短的回答是你不能这样做.答案很长,你可以模拟它,但要注意你必须创建大量的代码作为解决方法(实际上,我认为没有一个优雅的解决方案).

在讨论中,使用operator-提供了一个非常复杂的解决方法(在我看来这是个坏主意).我想死链接中提供的解决方案更不相似(因为它基于运算符|).

这基于能够与运算符的扩展方法做或多或少相同的能力.例如,如果你想为你的新类Foo重载ostream的operator <<,你可以这样做:

class Foo {
    friend ostream &operator<<(ostream &o, const Foo &foo);
    // more things...
};

ostream &operator<<(ostream &o, const Foo &foo)
{
  // write foo's info to o
}
Run Code Online (Sandbox Code Playgroud)

正如我所说,这是C++中唯一可用于扩展方法的类似机制.如果您可以自然地将您的函数转换为重载运算符,那么它很好.唯一的另一种可能性是人为地超载与您的目标无关的运算符,但这会让您编写非常混乱的代码.

我能想到的最相似的方法是创建一个扩展类并在那里创建新方法.不幸的是,这意味着你需要"适应"你的对象:

class stringext {
public:
    stringext(std::string &s) : str( &s )
        {}
    string trim()
        {  ...; return *str; }
private:
    string * str;
};
Run Code Online (Sandbox Code Playgroud)

然后,当你想做那些事情时:

void fie(string &str)
{
    // ...
    cout << stringext( str ).trim() << endl;
}
Run Code Online (Sandbox Code Playgroud)

如上所述,这并不完美,我认为不存在那种完美的解决方案.抱歉.

  • 我认为你基本上建议采用BOOST C++库的方法,由@Akira Takahashi在同一页中解释.请注意操作员| 用于按位OR运算符,并且您正在改变运算符的自然含义,可能会给代码的未来读者带来混淆.这就是为什么我认为这不是一个优雅的解决方案.一个真正优雅的解决方案是以类似于C#的方式引入扩展方法:http://msdn.microsoft.com/en-us/library/vstudio/bb383977.aspx (4认同)

den*_*s90 7

这是我在C++中看到的最接近扩展方法的东西.我个人喜欢它的使用方式,也许这就是我们最接近这种语言的扩展方法.但是有一些缺点:

  • 实施起来可能很复杂
  • 运算符优先级有时可能不那么好,这可能会导致意外

一个办法:

#include <iostream>

using namespace std;


class regular_class {

    public:

        void simple_method(void) const {
            cout << "simple_method called." << endl;
        }

};


class ext_method {

    private:

        // arguments of the extension method
        int x_;

    public:

        // arguments get initialized here
        ext_method(int x) : x_(x) {

        }


        // just a dummy overload to return a reference to itself
        ext_method& operator-(void) {
            return *this;
        }


        // extension method body is implemented here. The return type of this op. overload
        //    should be the return type of the extension method
        friend const regular_class& operator<(const regular_class& obj, const ext_method& mthd) {

            cout << "Extension method called with: " << mthd.x_ << " on " << &obj << endl;
            return obj;
        }
};


int main()
{ 
    regular_class obj;
    cout << "regular_class object at: " << &obj << endl;
    obj.simple_method();
    obj<-ext_method(3)<-ext_method(8);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这不是我的个人发明,最近我的一个朋友把它寄给了我,他说他是从大学邮件列表中得到的.