创建一个 class 的所有构造函数的类型列表

Creating a type list of all constructors of a class

我正在尝试实现惰性初始化类 lazy<T>,它存储 T 的构造参数并在第一次需要时使用它们构造 T。目前我正在捕获一个函数对象(存储在 boost::function 中)中的参数,该函数对象在调用时在指定位置构造对象;虽然这可行,但它需要动态分配,我想避免这种情况。

我想使用 boost::variant,其中不同的变体是对应于 T 的各种构造函数(假设没有任何模板构造函数)和 T 本身,以便在编译时知道所需的内存量。为此,我需要一种编译时方法来 assemble T 中所有可访问构造函数的列表。有办法吗?

这个答案有2个半版本:

  • 基于variant<>
  • 基于 optional<>
  • 一个实际上不采用构造函数参数但采用可调用工厂的版本。这可能会减少动态分配(但与问题有点偏差)

增强变量

更新 感谢@5gon12eder 的评论,我创建了这个基于 Boost Variant 的更优雅的版本:

请注意,Boost Variant 也使用动态内存分配。

Live On Coliru

#include <functional>
#include <boost/variant.hpp>

template <typename T>
struct Lazy {
    template <typename... Args> Lazy(Args const&... args) 
        : _storage(Factory([=] { return T(args...); }))
    { }

    T& get()             { return ensure(); }
    T const& get() const { return ensure(); }

    explicit operator bool() const { return _storage.which(); }

  private:
    using Factory = std::function<T()>;
    mutable boost::variant<Factory, T> _storage;

    T& ensure() const {
        if (!_storage.which())
            _storage = boost::get<Factory>(_storage)();

        return boost::get<T>(_storage);
    }
};

#include <string>
#include <iostream>

int main() {
    Lazy<std::string> ls("Hello world");
    std::cout << ls.get() << "\n";

    // Lazy<std::string> oops(42.7, true); // no matching function call
}

限制与 Boost Optional 一样(见下文)。请注意,对于变体,工厂将在实例化时被破坏,因此如果参数类型具有昂贵的析构函数,这可能是一个需要牢记的效率因素。

提升可选

When To Use Boost Optional

[...]

Another typical situation is to indicate that we do not have a value yet, but we expect to have it later. This notion can be used in implementing solutions like lazy initialization or a two-phase initialization.

请注意,Boost Optional 使用动态内存分配。

Live On Coliru

#include <functional>
#include <boost/optional.hpp>

template <typename T>
struct Lazy {
    template <typename... Args> Lazy(Args const&... args) 
        : _factory([=] { return T(args...); })
    { }

    T& get()             { return *(_value? _value : _value = _factory()); }
    T const& get() const { return *(_value? _value : _value = _factory()); }

    explicit operator bool() const { return _value; }

  private:
    std::function<T()> _factory;
    mutable boost::optional<T> _value;
};

#include <string>
#include <iostream>

int main() {
    Lazy<std::string> ls("Hello world");
    std::cout << ls.get() << "\n";

    // Lazy<std::string> oops(42.7, true); // no matching function call
}

诚然,这有局限性

  • 没有正确推导大括号初始化列表,
  • 构造函数参数必须是可复制的(使用 c++14 时可以不费吹灰之力解除此限制)
  • 类型擦除的 function<> 可能仍会导致动态分配。一些实现针对小型工厂仿函数优化了它,使用小型对象缓冲区优化,所以 profile, profile, profile

    您可以考虑直接从调用者提供工厂仿函数:
    请参阅此变体 Live On Coliru

    • 在某种程度上,这只是转移了负担——这可能重要也可能不重要
    • 在呼叫站点上使用似乎不太友好

还请注意,它会及早检测构造函数参数,例如

Lazy<std::string> oops(42.7, true);

根本无法编译。

作为奖励,它具有到 bool 的类似智能指针的上下文转换。你会想要添加更多的东西(比如 hash<> 特化,可能还有只是遵从 boost::optionals IO 运算符实现的 IO 运算符)