转换构造函数的隐式参数

问题描述:

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

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

这里是一个小的演示:

#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; 
} 

这可能吗?

编辑:我想澄清我为什么(到目前为止)不考虑全局变量。业主必须在某个时候由建筑商的用户手动发布,因此我无法创建一个专案。因此,用户可能完全“忘记”所有者。为了避免这种情况,我正在寻找一种方式来强化typechecker的所有者的存在。

+0

我想你说的是某种形式的'全球variable' –

+0

您可以设置'Owner' RAII静态/全局变量,并使用'ExprBuilder'是全局变量。 – Jarod42

+0

您的意思是使用unique_ptr作为全局变量以确保它仅用于上下文中并且必须明确保留?如果指针被用户遗忘,会不会产生运行时错误? – choeger

没有全局/静态变量,这几乎是不可能的,因为没有全局/静态信息,局部变量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代码,你可以省略的Owner构造函数/析构函数的类ScopedCurrentOwnerLock直接设置/取消指针。

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

  • 如果业主超出范围锁超出范围之前,你有一个无效的指针。

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

你所有的ExprBuildersOwner一个扶养,和你正确不想全局状态。所以你必须将所有者传递给每个构造函数。

如果你真的不想将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"); 
}