转换构造函数的隐式参数

Implicit argument to conversion constructors

本文关键字:参数 构造函数 转换      更新时间:2023-10-16

tl;dr:有没有办法将当前范围的默认参数添加到C++中的所有隐式构造函数?

我目前正在为C++的嵌入式语言设计一个界面。目标是使创建语法正确的表达式既类型安全又方便。现在,我认为学习像boost::p roto这样的重量级实现会在开发中产生太大的延迟,所以我尝试推出自己的实现。

这是一个小演示:

#include <iostream>
#include <string>
#include <sstream>
class ExprBuilder
{
public:
ExprBuilder(const int val) : val(std::to_string(val)) {}
ExprBuilder(const std::string val) : val(val) {}
ExprBuilder(const char* val) : val(val) {}
ExprBuilder(const ExprBuilder& lhs, const ExprBuilder& arg) {
std::stringstream ss;
ss << "(" << lhs.val << " " << arg.val << ")";
val = ss.str();
}
const ExprBuilder operator()(const ExprBuilder& l) const {
return ExprBuilder(*this, l);
}
template<typename... Args>
const ExprBuilder operator()(const ExprBuilder& arg, Args... args) const
{
return (*this)(arg)(args...) ;
}
std::string val;
};
std::ostream& operator<<(std::ostream& os, const ExprBuilder& e)
{
os << e.val;
return os;
}
int main() {
ExprBuilder f("f");
std::cout << f(23, "foo", "baz") << std::endl;
}

如您所见,由于C++重载和隐式转换,嵌入表达式相当简单。

然而,我面临着一个实际问题:在上面的例子中,所有数据都以 std::string 对象的形式分配。在实践中,我需要更复杂的东西(AST 节点),这些节点在堆上分配并由专用所有者管理(遗留代码,无法更改)。所以我必须传递一个唯一的参数(所述所有者)并将其用于分配。我宁愿不在这里使用静态字段。

我正在搜索的是一种使用方式,要求用户在每次使用构建器时提供这样的所有者,但要以方便的方式。像动态范围的变量这样的东西会很棒。有没有办法在C++中获得以下内容:

class ExprBuilder
{
...
ExprBuilder(const ExprBuilder& lhs, const ExprBuilder& arg) {
return ExprBuilder(owner.allocate(lhs, rhs)); // use the owner implicitly
}
...
};
int main() {
Owner owner; // used in all ExprBuilder instances in the current scope
ExprBuilder f("f");
std::cout << f(23, "foo", "baz") << std::endl;
}

这可能吗?

编辑:我想澄清为什么我(到目前为止)不考虑全局变量。所有者必须在某个时候由构建器的用户手动释放,因此我无法创建一个临时。因此,用户可能会完全"忘记"所有者。为了避免这种情况,我正在寻找一种方法来强制类型检查器存在所有者。

如果没有全局/静态变量,这几乎是不可能的,因为没有全局/静态信息,局部变量Owner ownerExprBuilder f无法知道彼此的任何信息。

我认为最干净的方法是添加一个

static Owner* current_owner;

ExprBuilder类。然后,您可以添加一个新的类ScopedCurrentOwnerLock,它将构造函数中的current_owner设置为析构函数中的 nullptr。然后你可以像互斥锁一样使用它:

class ScopedCurrentOwnerLock {
public:
ScopedCurrentOwnerLock(Owner const& owner) {
ExprBuilder::current_owner = &owner;
}
~ScopedCurrentOwnerLock() {
ExprBuilder::current_owner = nullptr;
}
};
int main() {
Owner owner;
ScopedCurrentOwnerLock lock(owner);
ExprBuilder f("f");
}

如果你可以访问Owner代码,你可以省略ScopedCurrentOwnerLock类,直接在Owner的构造函数/析构函数中设置/取消设置指针。

请注意此解决方案的以下两个问题:

  • 如果所有者在锁超出范围之前超出范围,则您的指针无效。

  • 如果同时有多个锁,则静态指针具有不可预测的行为,例如由于多线程。

你所有的ExprBuilders都依赖于Owner,你理所当然地不想要全局状态。所以你必须将所有者传递给每个构造函数。

如果您真的不想将owner,添加到块中的所有实例中,则可以创建一个工厂来为您传递它。

struct ExprBuilderFactory
{
Owner & owner;
ExprBuilder operator()(int val) { return ExprBuilder(owner, val); }
ExprBuilder operator()(char * val) { return ExprBuilder(owner, val); }
// etc
}
int main() {
Owner owner;
ExprBuilderFactory factory{ owner };
ExprBuilder f = factory("f");
}