【问题标题】:lambda return in initializer list初始化列表中的 lambda 返回
【发布时间】:2014-12-10 12:39:23
【问题描述】:

让我们看一个现实生活中的例子:

class RuleNameConverter {
public:
    RuleNameConverter(const boost::property_tree::ptree& pt);
    int toIdentifier(const std::string& name) const;
    std::string toName(const int id) const;

private:
    using Bimap = boost::bimap<std::string, int>;
    Bimap bimap_;
};

构造函数在哪里:

RuleNameConverter::RuleNameConverter(const boost::property_tree::ptree& pt) {
    for (const auto& item : pt) {
        if (item.first == "rule") {
            auto name = item.second.get < std::string > ("<xmlattr>.name");
            auto id = item.second.get<int>("<xmlattr>.id");
            bimap_.insert(Bimap::value_type { name, id });
        }
    }
}

假设你想要一个 const 成员属性:

...
    const Bimap bimap_;
};

你必须在初始化列表中初始化它,而不是在构造函数体中。它的初始化很重要,所以你必须委托一个函数来计算它的值。您可以使用 lambda 返回的值,利用移动语义(没有临时对象的副本):

RuleNameConverter::RuleNameConverter(const boost::property_tree::ptree& pt) :
        bimap_ { [&pt]() {
            Bimap results;
            for (const auto& item : pt) {
                if (item.first == "rule") {
                    auto name = item.second.get < std::string > ("<xmlattr>.name");
                    auto id = item.second.get<int>("<xmlattr>.id");
                    results.insert(Bimap::value_type {name, id});
                }
            }
            return results;
        }() } {
}

使用这种技术有什么缺点吗?值得麻烦吗?我觉得它的可读性稍差,但性能呢?

【问题讨论】:

  • 嗯。我会仔细检查捕获范围规则,以确保那里没有怪癖(你处于一个奇怪的环境中)——可能不是,在 2 个标准修订之后,尤其是在实践中。
  • return results; 更改为return std::move(results);。是的,编译器有 RVO,但这种方式很冗长。
  • @GreenScape 我记得 C++11 标准在这种情况下需要 RVO,对吧?
  • C++11 不需要 RVO,但如果不执行 RVO,它确实需要尝试使用移动构造函数。绝对没有理由写return std::move(results);
  • @hvd,在return results; 中究竟是如何调用移动运算符的?

标签: c++ c++11 lambda initializer-list


【解决方案1】:

就性能而言,它应该没那么重要。您不会复制任何 Bitmap 对象,并且您的 lambda 构造不应花费任何明显的时间。

但为了可读性,我会在这里创建一个静态成员函数而不是 lambda:

class RuleNameConverter {
public:
    RuleNameConverter(const boost::property_tree::ptree& pt);
private:
    static Bitmap createBitmap(const boost::property_tree::ptree& pt);
};

RuleNameConverter::RuleNameConverter(const boost::property_tree::ptree& pt) :
    bimap_ { createBitmap(pt) } {
}

Bitmap RuleNameConverter::createBitmap(const boost::property_tree::ptree& pt) {
    Bimap results;
    for (const auto& item : pt) {
        if (item.first == "rule") {
            auto name = item.second.get < std::string > ("<xmlattr>.name");
            auto id = item.second.get<int>("<xmlattr>.id");
            results.insert(Bimap::value_type {name, id});
        }
    }
    return results;
}

当您需要使用辅助函数初始化多个成员时,为每个成员创建一个新的 lambda 会导致构造函数初始化器列表中出现无法维护的混乱,但多个辅助函数不需要有这个问题。此外,如果添加构造函数重载,createBitmap 可以很容易地从多个构造函数中调用。

或者,如果createBitmap 的主体不是真正特定于您的RuleNameConverter,则使用常规的非成员函数。

【讨论】:

    【解决方案2】:

    您可以将 Bimap 包装在另一个类中,其中它的构造函数将具有与 lambda 完全相同的主体。

    我看不出在这种情况下如何使用 lambda 来避免肤浅的类会导致任何问题,除了它的意图可能不太清楚,因为它没有名称(但几乎就是这种情况任何 lambda)。

    【讨论】:

      猜你喜欢
      • 2018-07-15
      • 2018-03-02
      • 1970-01-01
      • 2018-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多