C++ 中的扩展方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5463009/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Extension methods in c++
提问by Bob
I was searching for an implementation of extension methods in c++ and came upon this comp.std.c++ discussionwhich mentions that polymorphic_map
can be used to associated methods with a class, but, the provided link seems to be dead. Does anyone know what that answer was referring to, or if there is another way to extend classes in a similar manner to extension methods (perhaps through some usage of mixins?).
我正在寻找 c++ 中扩展方法的实现,并发现了这个 comp.std.c++ 讨论,其中提到polymorphic_map
可用于将方法与类相关联,但是,提供的链接似乎已失效。有谁知道那个答案指的是什么,或者是否有另一种方法以与扩展方法类似的方式扩展类(也许通过使用 mixin?)。
I know the canonical C++ solution is to use free functions; this is more out of curiosity than anything else.
我知道规范的 C++ 解决方案是使用自由函数;这更多是出于好奇。
回答by David Rodríguez - dribeas
Different languages approach development in different ways. In particular C# and Java have a strong point of view with respect to OO that leads to everything is an objectmindset (C# is a little more lax here). In that approach, extension methods provide a simple way of extending an existing object or interface to add new features.
不同的语言以不同的方式进行开发。特别是 C# 和 Java 对 OO 有很强的观点,这导致一切都是对象思维方式(C# 在这里有点松懈)。在这种方法中,扩展方法提供了一种扩展现有对象或接口以添加新功能的简单方法。
There are no extension methods in C++, nor are they needed. When developing C++, forget the everything is an object paradigm --which, by the way, is false even in Java/C# [*]. A different mindset is taken in C++, there are objects, and the objects have operations that are inherently part of the object, but there are also other operations that form part of the interface and need not be part of the class. A must read by Herb Sutter is What's In a Class?, where the author defends (and I agree) that you can easily extend any given class with simple free functions.
C++ 中没有扩展方法,也不需要它们。在开发 C++ 时,忘记一切都是对象范式——顺便说一下,即使在 Java/C# [*] 中也是错误的。在 C++ 中采用了不同的思维方式,有对象,并且对象的操作本质上是对象的一部分,但也有其他操作构成接口的一部分,不需要属于类的一部分。赫伯·萨特 (Herb Sutter) 必读的一本书是课堂上有什么?,作者捍卫(并且我同意)您可以使用简单的自由函数轻松扩展任何给定的类。
As a particular simple example, the standard templated class basic_ostream
has a few member methods to dump the contents of some primitive types, and then it is enhanced with (also templated) free functions that extend that functionality to other types by using the existing public interface. For example, std::cout << 1;
is implemented as a member function, while std::cout << "Hi";
is a free function implemented in terms of other more basic members.
作为一个特别简单的例子,标准模板化类basic_ostream
有一些成员方法来转储一些基本类型的内容,然后它被增强(也是模板化的)自由函数,通过使用现有的公共接口将该功能扩展到其他类型。例如,std::cout << 1;
作为成员函数实现,而std::cout << "Hi";
在其他更基本的成员方面实现的自由函数。
Extensibility in C++ is achieved by means of free functions, not by ways of adding new methods to existing objects.
C++ 中的可扩展性是通过自由函数实现的,而不是通过向现有对象添加新方法的方式来实现的。
[*] Everything is notan object.
[*] 一切都不是对象。
In a given domain will contain a set of actual objects that can be modeled and operations that can be applied to them, in some cases those operations will be part of the object, but in some other cases they will not. In particular you will find utility classesin the languages that claim that everything is an object and those utility classesare nothing but a layer trying to hide the fact that those methods don't belong to any particular object.
在给定的域中将包含一组可以建模的实际对象和可以应用于它们的操作,在某些情况下,这些操作将是对象的一部分,但在其他一些情况下它们不会。特别是您会发现语言中的实用程序类声称一切都是对象,而这些实用程序类只不过是一层试图隐藏这些方法不属于任何特定对象的事实的层。
Even some operations that are implemented as member functions are not really operations on the object. Consider addition for a Complex
number class, how is sum
(or +
) more of an operation on the first argument than the second? Why a.sum(b);
or b.sum(a)
, should it not be sum( a, b )
?
甚至一些作为成员函数实现的操作也不是真正对对象的操作。考虑一个Complex
数字类的加法,如何sum
(或+
)对第一个参数的运算比第二个参数多?为什么a.sum(b);
或者b.sum(a)
,不应该sum( a, b )
?
Forcing the operations to be member methods actually produces weird effects --but we are just used to them: a.equals(b);
and b.equals(a);
might have completely different results even if the implementation of equals
is fully symmetric. (Consider what happens when either a
or b
is a null pointer)
强制操作成为成员方法实际上会产生奇怪的效果——但我们只是习惯了它们:即使 的实现是完全对称的a.equals(b);
,b.equals(a);
也可能有完全不同的结果equals
。(考虑当a
或b
是空指针时会发生什么)
回答by Akira Takahashi
Boost Range Library's approach use operator|().
Boost Range Library 的方法使用 operator|()。
r | filtered(p);
I can write trim for string as follows in the same way, too.
我也可以同样地如下写字符串的修剪。
#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;
}
回答by Baltasarq
The short answer is that you cannot do that. The long answer is that you can simulate it, but be aware that you'll have to create a lot of code as workaround (actually, I don't think there is an elegant solution).
简短的回答是你不能那样做。长的答案是您可以模拟它,但请注意,您必须创建大量代码作为解决方法(实际上,我认为没有优雅的解决方案)。
In the discussion, a very complex workaround is provided using operator- (which is a bad idea, in my opinion). I guess that the solution provided in the dead link was more o less similar (since it was based on operator|).
在讨论中,使用 operator- 提供了一个非常复杂的解决方法(在我看来,这是一个坏主意)。我猜死链接中提供的解决方案更相似(因为它基于运算符|)。
This is based in the capability of being able to do more or less the same thing as an extension method with operators. For example, if you want to overload the ostream's operator<< for your new class Foo, you could do:
这是基于能够或多或少地与操作符的扩展方法做相同的事情的能力。例如,如果你想为你的新类 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
}
As I said, this is the only similar mechanism availabe in C++ for extension methods. If you can naturally translate your function to an overloaded operator, then it is fine. The only other possibility is to artificially overload an operator that has nothing to do with your objective, but this is going to make you write very confusing code.
正如我所说,这是 C++ 中唯一可用于扩展方法的类似机制。如果您可以自然地将函数转换为重载运算符,那就没问题了。唯一的另一种可能性是人为地重载与您的目标无关的运算符,但这会使您编写非常混乱的代码。
The most similar approach I can think of would mean to create an extension class and create your new methods there. Unfortunately, this means that you'll need to "adapt" your objects:
我能想到的最相似的方法是创建一个扩展类并在那里创建新方法。不幸的是,这意味着您需要“适应”您的对象:
class stringext {
public:
stringext(std::string &s) : str( &s )
{}
string trim()
{ ...; return *str; }
private:
string * str;
};
And then, when you want to do that things:
然后,当你想做那些事情时:
void fie(string &str)
{
// ...
cout << stringext( str ).trim() << endl;
}
As said, this is not perfect, and I don't think that kind of perfect solution exists. Sorry.
如前所述,这并不完美,我认为这种完美的解决方案不存在。对不起。
回答by dennis90
This is the closest thing that I have ever seen to extension methods in C++. Personally i like the way it can be used, and possibly this it the closest we can get to extension methods in this language. But there are some disadvantages:
这是我见过的最接近 C++ 扩展方法的东西。我个人喜欢它的使用方式,这可能是我们最接近这种语言中的扩展方法的方式。但也有一些缺点:
- It may be complicated to implement
- Operator precedence may be not that nice some times, this may cause surprises
- 实施起来可能很复杂
- 运算符优先级有时可能不是那么好,这可能会引起意外
A solution:
一个办法:
#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;
}
This is not my personal invention, recently a friend of mine mailed it to me, he said he got it from a university mailing list.
这不是我个人的发明,最近我的一个朋友寄给我,他说他是从大学邮件列表中得到的。
回答by ceztko
To elaborate more on @Akira answer, operator|
can be used to extend existing classes with functions that take parameters too. Here an example that I'm using to extend Xerces XML library with find functionalities that can be easily concatenated:
要详细说明@Akira 的答案,operator|
可用于使用也带有参数的函数来扩展现有类。这是我用来扩展 Xerces XML 库的示例,其中包含可以轻松连接的查找功能:
#pragma once
#include <string>
#include <stdexcept>
#include <xercesc/dom/DOMElement.hpp>
#define _U16C // macro that converts string to char16_t array
XERCES_CPP_NAMESPACE_BEGIN
struct FindFirst
{
FindFirst(const std::string& name);
DOMElement * operator()(const DOMElement &el) const;
DOMElement * operator()(const DOMElement *el) const;
private:
std::string m_name;
};
struct FindFirstExisting
{
FindFirstExisting(const std::string& name);
DOMElement & operator()(const DOMElement &el) const;
private:
std::string m_name;
};
inline DOMElement & operator|(const DOMElement &el, const FindFirstExisting &f)
{
return f(el);
}
inline DOMElement * operator|(const DOMElement &el, const FindFirst &f)
{
return f(el);
}
inline DOMElement * operator|(const DOMElement *el, const FindFirst &f)
{
return f(el);
}
inline FindFirst::FindFirst(const std::string & name)
: m_name(name)
{
}
inline DOMElement * FindFirst::operator()(const DOMElement &el) const
{
auto list = el.getElementsByTagName(_U16C(m_name));
if (list->getLength() == 0)
return nullptr;
return static_cast<DOMElement *>(list->item(0));
}
inline DOMElement * FindFirst::operator()(const DOMElement *el) const
{
if (el == nullptr)
return nullptr;
auto list = el->getElementsByTagName(_U16C(m_name));
if (list->getLength() == 0)
return nullptr;
return static_cast<DOMElement *>(list->item(0));
}
inline FindFirstExisting::FindFirstExisting(const std::string & name)
: m_name(name)
{
}
inline DOMElement & FindFirstExisting::operator()(const DOMElement & el) const
{
auto list = el.getElementsByTagName(_U16C(m_name));
if (list->getLength() == 0)
throw runtime_error(string("Missing element with name ") + m_name);
return static_cast<DOMElement &>(*list->item(0));
}
XERCES_CPP_NAMESPACE_END
It can be used this way:
它可以这样使用:
auto packetRate = *elementRoot | FindFirst("Header") | FindFirst("PacketRate");
auto &decrypted = *elementRoot | FindFirstExisting("Header") | FindFirstExisting("Decrypted");
回答by KindElk
You can enable kindaextension methods for your own class/struct or for some specific type in some scope. See rough solution below.
您可以启用还挺为自己的类/结构或在某些范围内某些特定类型的扩展方法。请参阅下面的粗略解决方案。
class Extensible
{
public:
template<class TRes, class T, class... Args>
std::function<TRes(Args...)> operator|
(std::function<TRes(T&, Args...)>& extension)
{
return [this, &extension](Args... args) -> TRes
{
return extension(*static_cast<T*>(this), std::forward<Args>(args)...);
};
}
};
Then inherit your class from this and use like
然后从这个继承你的类并使用像
class SomeExtensible : public Extensible { /*...*/ };
std::function<int(SomeExtensible&, int)> fn;
SomeExtensible se;
int i = (se | fn)(4);
Or you can declare this operator in cpp file or namespace.
或者您可以在 cpp 文件或命名空间中声明此运算符。
//for std::string, for example
template<class TRes, class... Args>
std::function<TRes(Args...)> operator|
(std::string& s, std::function<TRes(std::string&, Args...)>& extension)
{
return [&s, &extension](Args... args) -> TRes
{
return extension(s, std::forward<Args>(args)...);
};
}
std::string s = "newStr";
std::function<std::string(std::string&)> init = [](std::string& s) {
return s = "initialized";
};
(s | init)();
Or even wrap it in macro (I know, it's generally bad idea, nevertheless you can):
或者甚至将它包装在宏中(我知道,这通常是个坏主意,但您可以):
#define ENABLE_EXTENSIONS_FOR(x) \
template<class TRes, class... Args> \
std::function<TRes(Args...)> operator| (x s, std::function<TRes(x, Args...)>& extension) \
{ \
return [&s, &extension](Args... args) -> TRes \
{ \
return extension(s, std::forward<Args>(args)...); \
}; \
}
ENABLE_EXTENSIONS_FOR(std::vector<int>&);