当 'operator' 函数是根据 class 而不是实际运算符定义时,它们如何工作?
How do 'operator' functions work when they're defined in terms of a class rather than an actual operator?
我正在探索有关 C++ 设计模式的在线课程,我遇到了一个奇怪的 "cast" (?) 使用 operator
函数声明。
最小设置如下(实际代码如下):
class A {
...
static B build();
};
class B {
A a;
};
int main()
{
A obj = A::build();
}
由于 build
函数 return 是类型 B
的对象,因此存在类型不匹配,代码无法编译。为了纠正这个问题,教师在 class B:
中定义了以下函数
operator A() { return a; }
我的问题是,这是如何工作的?我了解重载运算符的机制,但在这种情况下,我们重载的是实际的 class,而不是运算符。当我们使用另一个 class 声明一个运算符函数时发生了什么?而且,没有定义 return 类型,编译器是否只是假设 return 类型与定义函数的 class 类型相同? (即... B operator A() { ... }
)直觉上我无法真正理解这个概念。
我根本没听说过这种方法,更别提在刚才遇到之前认为它是可能的了。我一直在尝试在线研究这个问题,但是 - 可以理解,我会说 - 我所有的搜索结果 return 基本重载链接,或者至少是更传统的重载,使用运算符。
就上下文而言,本讲座是关于 "Builder" 设计模式,使用 Html 元素和 Html 构建器结构。这是我的基本代码,还没有修改。
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
struct HtmlBuilder;
struct HtmlElement {
std::string name;
std::string text;
std::vector<HtmlElement> elements;
const std::size_t indent_size = 2;
std::string str(const int indent = 0) const {
std::ostringstream oss;
std::string indentation(indent_size * indent, ' ');
oss << indentation << "<" << name << ">\n";
if (!text.empty())
oss << std::string(indent_size * (indent + 1), ' ') << text << '\n';
for (const auto& element : elements)
oss << element.str(indent + 1);
oss << indentation << "</" << name << ">\n";
return oss.str();
}
static HtmlBuilder build(const std::string& rootName);
};
struct HtmlBuilder {
HtmlElement root;
void addChild(const std::string& childName, const std::string& childText) {
HtmlElement childElement { childName, childText };
root.elements.emplace_back(childElement);
}
std::string str() const { return root.str(); }
};
HtmlBuilder HtmlElement::build(const std::string& rootName) {
return { rootName };
}
int main()
{
HtmlBuilder builder { "ul" };
builder.addChild("li", "hello");
builder.addChild("li", "world");
std::cout << builder.str();
}
输出,如预期:
<ul>
<li>
hello
</li>
<li>
world
</li>
</ul>
在演示 "fluent builder" 模式时,讲师让我们将 addChild
函数修改为 return 对构建器结构的引用。
HtmlBuilder::addChild
函数修改如下:return类型由void
改为HtmlBuilder&
(returning *this
)
HtmlBuilder& addChild(const std::string& childName, const std::string& childText) {
HtmlElement childElement { childName, childText };
root.elements.emplace_back(childElement);
return *this;
}
然后重写main
函数:
int main()
{
auto builder = HtmlElement::build("ul").addChild("li", "hello").addChild("li", "world");
std::cout << builder.str();
}
输出又是:
<ul>
<li>
hello
</li>
<li>
world
</li>
</ul>
成功定义并实施了流畅的构建器模式后,讲师现在提出以下问题:
How could we get an Html element object from our build
function?
我的第一反应是考虑为 HtmlBuilder
class 提供一个 getter 方法。一些琐碎的事情,像这样:
struct HtmlBuilder {
...
HtmlElement getElement() const { return root; }
};
然后您将 "build and get" 元素像这样:
int main()
{
const auto builder = HtmlElement::build("ul").addChild("li", "hello").addChild("li", "world");
const auto element = builder.getElement();
std::cout << builder.str() << '\n';
std::cout << element.str() << '\n';
}
两个输出将是相同的。然而,导师选择了一种截然不同且有趣得多的方法。他没有通过我的 "build and get" 方法分两步完成,而是执行了以下操作。
他首先像这样重写了 main
函数(请注意,他一步构建和获取元素,这与我不同):
int main()
{
HtmlElement element = HtmlElement::build("ul").addChild("li", "hello").addChild("li", "world");
std::cout << element.str();
}
最初编译器拒绝此修改,因为 HtmlElement::build
调用的结果是一个 HtmlBuilder
对象。所以为了解决这个问题,导师做的第二件事就是在HtmlBuilder
class中定义如下函数:
operator HtmlElement() const { return root; }
完成后,代码编译顺利,应用程序输出再次为:
<ul>
<li>
hello
</li>
<li>
world
</li>
</ul>
同样,我的问题是,为什么或如何工作?当我们使用另一个 class 声明一个 operator
函数时发生了什么?我了解通常的运算符重载的阴谋。重载 ()
、[]
或 =
对我来说很直观,但我不明白这种情况如何或为何起作用。甚至没有声明的 return 类型;编译器是否只是假设它意味着 return 当前 class 类型?
谢谢大家的宝贵时间。
那是 user-defined conversion。 return 类型是 operator
之后的类型,即目标类型。这为类型添加了一个额外的隐式转换,每当考虑隐式转换时都会使用它。
使用 operator
这个词确实不是最清晰的关键字,因为它 真的 没有定义运算符(尽管它可以与强制转换操作交互), 但我想这是为了避免添加另一个保留字。
回复:"we're overloading an actual class"。不,operator A() { return a; }
正在重载运算符;注意关键字 operator
。这定义了一个 转换运算符 ,当代码调用从类型 B
的对象到类型 A
.[=27 的对象的转换时,将使用该运算符=]
您的示例中的用法有点晦涩。这是一个更简单的例子:
B b;
A obj = b;
创建 obj
对象需要将 b
对象转换为类型 A
的对象,这就是 operator A()
所做的工作。
在您的示例中,调用 A::build
returns 类型为 B
的对象,因此在代码中
A obj = A::build();
调用 A::build()
returns 类型为 B
的临时对象,转换运算符 (operator A()
) 将该对象转换为类型为 A
,用来初始化obj
.
我正在探索有关 C++ 设计模式的在线课程,我遇到了一个奇怪的 "cast" (?) 使用 operator
函数声明。
最小设置如下(实际代码如下):
class A {
...
static B build();
};
class B {
A a;
};
int main()
{
A obj = A::build();
}
由于 build
函数 return 是类型 B
的对象,因此存在类型不匹配,代码无法编译。为了纠正这个问题,教师在 class B:
operator A() { return a; }
我的问题是,这是如何工作的?我了解重载运算符的机制,但在这种情况下,我们重载的是实际的 class,而不是运算符。当我们使用另一个 class 声明一个运算符函数时发生了什么?而且,没有定义 return 类型,编译器是否只是假设 return 类型与定义函数的 class 类型相同? (即... B operator A() { ... }
)直觉上我无法真正理解这个概念。
我根本没听说过这种方法,更别提在刚才遇到之前认为它是可能的了。我一直在尝试在线研究这个问题,但是 - 可以理解,我会说 - 我所有的搜索结果 return 基本重载链接,或者至少是更传统的重载,使用运算符。
就上下文而言,本讲座是关于 "Builder" 设计模式,使用 Html 元素和 Html 构建器结构。这是我的基本代码,还没有修改。
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
struct HtmlBuilder;
struct HtmlElement {
std::string name;
std::string text;
std::vector<HtmlElement> elements;
const std::size_t indent_size = 2;
std::string str(const int indent = 0) const {
std::ostringstream oss;
std::string indentation(indent_size * indent, ' ');
oss << indentation << "<" << name << ">\n";
if (!text.empty())
oss << std::string(indent_size * (indent + 1), ' ') << text << '\n';
for (const auto& element : elements)
oss << element.str(indent + 1);
oss << indentation << "</" << name << ">\n";
return oss.str();
}
static HtmlBuilder build(const std::string& rootName);
};
struct HtmlBuilder {
HtmlElement root;
void addChild(const std::string& childName, const std::string& childText) {
HtmlElement childElement { childName, childText };
root.elements.emplace_back(childElement);
}
std::string str() const { return root.str(); }
};
HtmlBuilder HtmlElement::build(const std::string& rootName) {
return { rootName };
}
int main()
{
HtmlBuilder builder { "ul" };
builder.addChild("li", "hello");
builder.addChild("li", "world");
std::cout << builder.str();
}
输出,如预期:
<ul>
<li>
hello
</li>
<li>
world
</li>
</ul>
在演示 "fluent builder" 模式时,讲师让我们将 addChild
函数修改为 return 对构建器结构的引用。
HtmlBuilder::addChild
函数修改如下:return类型由void
改为HtmlBuilder&
(returning *this
)
HtmlBuilder& addChild(const std::string& childName, const std::string& childText) {
HtmlElement childElement { childName, childText };
root.elements.emplace_back(childElement);
return *this;
}
然后重写main
函数:
int main()
{
auto builder = HtmlElement::build("ul").addChild("li", "hello").addChild("li", "world");
std::cout << builder.str();
}
输出又是:
<ul>
<li>
hello
</li>
<li>
world
</li>
</ul>
成功定义并实施了流畅的构建器模式后,讲师现在提出以下问题:
How could we get an Html element object from our
build
function?
我的第一反应是考虑为 HtmlBuilder
class 提供一个 getter 方法。一些琐碎的事情,像这样:
struct HtmlBuilder {
...
HtmlElement getElement() const { return root; }
};
然后您将 "build and get" 元素像这样:
int main()
{
const auto builder = HtmlElement::build("ul").addChild("li", "hello").addChild("li", "world");
const auto element = builder.getElement();
std::cout << builder.str() << '\n';
std::cout << element.str() << '\n';
}
两个输出将是相同的。然而,导师选择了一种截然不同且有趣得多的方法。他没有通过我的 "build and get" 方法分两步完成,而是执行了以下操作。
他首先像这样重写了 main
函数(请注意,他一步构建和获取元素,这与我不同):
int main()
{
HtmlElement element = HtmlElement::build("ul").addChild("li", "hello").addChild("li", "world");
std::cout << element.str();
}
最初编译器拒绝此修改,因为 HtmlElement::build
调用的结果是一个 HtmlBuilder
对象。所以为了解决这个问题,导师做的第二件事就是在HtmlBuilder
class中定义如下函数:
operator HtmlElement() const { return root; }
完成后,代码编译顺利,应用程序输出再次为:
<ul>
<li>
hello
</li>
<li>
world
</li>
</ul>
同样,我的问题是,为什么或如何工作?当我们使用另一个 class 声明一个 operator
函数时发生了什么?我了解通常的运算符重载的阴谋。重载 ()
、[]
或 =
对我来说很直观,但我不明白这种情况如何或为何起作用。甚至没有声明的 return 类型;编译器是否只是假设它意味着 return 当前 class 类型?
谢谢大家的宝贵时间。
那是 user-defined conversion。 return 类型是 operator
之后的类型,即目标类型。这为类型添加了一个额外的隐式转换,每当考虑隐式转换时都会使用它。
使用 operator
这个词确实不是最清晰的关键字,因为它 真的 没有定义运算符(尽管它可以与强制转换操作交互), 但我想这是为了避免添加另一个保留字。
回复:"we're overloading an actual class"。不,operator A() { return a; }
正在重载运算符;注意关键字 operator
。这定义了一个 转换运算符 ,当代码调用从类型 B
的对象到类型 A
.[=27 的对象的转换时,将使用该运算符=]
您的示例中的用法有点晦涩。这是一个更简单的例子:
B b;
A obj = b;
创建 obj
对象需要将 b
对象转换为类型 A
的对象,这就是 operator A()
所做的工作。
在您的示例中,调用 A::build
returns 类型为 B
的对象,因此在代码中
A obj = A::build();
调用 A::build()
returns 类型为 B
的临时对象,转换运算符 (operator A()
) 将该对象转换为类型为 A
,用来初始化obj
.