【问题标题】:Implicit argument to conversion constructors转换构造函数的隐式参数
【发布时间】:2017-03-16 12:08:35
【问题描述】:

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

这可能吗?

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

【问题讨论】:

  • 我觉得你说的是某种global variable
  • 您可以使用Owner Raii 设置一个静态/全局变量,并在ExprBuilder 中使用该全局变量。
  • 您的意思是使用 unique_ptr 作为全局变量以确保它仅在上下文中使用并且必须显式保留?如果用户忘记了指针,这不会产生运行时错误吗?
  • @appleapple OP 已经说过“我不想在这里使用静态字段”。静态成员变量是全局变量的一种特殊形式。我强烈怀疑他的反对意见适用于任何其他口味。
  • @choeger:我认为这个想法是你有一个全局变量(如果这是遗留代码,可能是一个指向某个东西的指针),然后是一个 RAII 类来设置该全局变量 并取消设置之后.

标签: c++ implicit-conversion implicit default-arguments dynamic-scope


【解决方案1】:

如果没有全局/静态变量,这几乎是不可能的,因为没有全局/静态信息,局部变量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的构造函数/析构函数中设置/取消设置指针。

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

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

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

【讨论】:

    【解决方案2】:

    你所有的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");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-19
      • 2023-03-17
      • 2013-11-02
      • 2020-06-20
      • 1970-01-01
      • 2014-02-24
      • 1970-01-01
      相关资源
      最近更新 更多