有没有一种非重复的方法允许程序员在成员初始化的复制和移动语义之间进行选择

Is there a non-repetitive way to allow the programmer to choose between copy and move semantics for member initialization?

本文关键字:复制 初始化 成员 移动 行选 选择 之间 语义 程序员 一种 方法      更新时间:2023-10-16

我希望能够使用移动语义或复制语义初始化类的每个字段。构造函数都将使用基本相同的代码进行构造,如下所示:

LogRecord::LogRecord(const Logger &logger, LogLevel level, const std::wstring &message)
    : level(level), logger_name(logger.GetName()), message(message), sequence_number(LogRecord::record_count++), source_class_name(), source_method_name(), time(std::chrono::system_clock::now()) {
}
LogRecord::LogRecord(const Logger &logger, LogLevel level, std::wstring &&message)
    : level(level), logger_name(logger.GetName()), message(message), sequence_number(LogRecord::record_count++), source_class_name(), source_method_name(), time(std::chrono::system_clock::now()) {
}
LogRecord::LogRecord(const Logger &logger, LogLevel level, const std::wstring &message, const std::wstring &source_class_name, const std::wstring &source_method_name)
    : level(level), logger_name(logger.GetName()), message(message), sequence_number(LogRecord::record_count++), source_class_name(source_class_name), source_method_name(source_method_name), time(std::chrono::system_clock::now()) {
}
LogRecord::LogRecord(const Logger &logger, LogLevel level, std::wstring &&message, const std::wstring &source_class_name, const std::wstring &source_method_name)
    : level(level), logger_name(logger.GetName()), message(message), sequence_number(LogRecord::record_count++), source_class_name(source_class_name), source_method_name(source_method_name), time(std::chrono::system_clock::now()) {
}
LogRecord::LogRecord(const Logger &logger, LogLevel level, const std::wstring &message, std::wstring &&source_class_name, const std::wstring &source_method_name)
    : level(level), logger_name(logger.GetName()), message(message), sequence_number(LogRecord::record_count++), source_class_name(source_class_name), source_method_name(source_method_name), time(std::chrono::system_clock::now()) {
}
LogRecord::LogRecord(const Logger &logger, LogLevel level, std::wstring &&message, std::wstring &&source_class_name, const std::wstring &source_method_name)
    : level(level), logger_name(logger.GetName()), message(message), sequence_number(LogRecord::record_count++), source_class_name(source_class_name), source_method_name(source_method_name), time(std::chrono::system_clock::now()) {
}

等。

有没有比简单地为每个可能的组合声明一个构造函数更好的方法呢?

class LogRecord {
public:
    LogRecord(const Logger &logger, LogLevel level, const std::wstring &message);
    LogRecord(const Logger &logger, LogLevel level, std::wstring &&message);
    LogRecord(const Logger &logger, LogLevel level, const std::wstring &message, const std::wstring &source_class_name, const std::wstring &source_method_name);
    LogRecord(const Logger &logger, LogLevel level, std::wstring &&message, const std::wstring &source_class_name, const std::wstring &source_method_name);
    LogRecord(const Logger &logger, LogLevel level, const std::wstring &message, std::wstring &&source_class_name, const std::wstring &source_method_name);
    LogRecord(const Logger &logger, LogLevel level, std::wstring &&message, std::wstring &&source_class_name, const std::wstring &source_method_name);
    LogRecord(const Logger &logger, LogLevel level, const std::wstring &message, const std::wstring &source_class_name, std::wstring &&source_method_name);
    LogRecord(const Logger &logger, LogLevel level, std::wstring &&message, const std::wstring &source_class_name, std::wstring &&source_method_name);
    LogRecord(const Logger &logger, LogLevel level, const std::wstring &message, std::wstring &&source_class_name, std::wstring &&source_method_name);
    LogRecord(const Logger &logger, LogLevel level, std::wstring &&message, std::wstring &&source_class_name, std::wstring &&source_method_name);
    ...
private:
    std::wstring message, source_class_name, source_method_name;
    ...
};

这是一个简化的形式,使其更容易阅读。 Object 是具有成员的类,Member是成员的类型名。Member类型同时定义了复制构造函数和移动构造函数。

基本上,我的问题是如何以更少的代码重复执行以下操作:

class Object {
public:
    Object(const Member &x, const Member &y, const Member &z) : x(x), y(y), z(z) {}
    Object(Member &&x, const Member &y, const Member &z) : x(x), y(y), z(z) {}
    Object(const Member &x, Member &&y, const Member &z) : x(x), y(y), z(z) {}
    Object(Member &&x, Member &&y, const Member &z) : x(x), y(y), z(z) {}
    Object(const Member &x, const Member &y, Member &&z) : x(x), y(y), z(z) {}
    Object(Member &&x, const Member &y, Member &&z) : x(x), y(y), z(z) {}
    Object(const Member &x, Member &&y, Member &&z) : x(x), y(y), z(z) {}
    Object(Member &&x, Member &&y, Member &&z) : x(x), y(y), z(z) {}
private:
    Member x, y, z;
}

我不会为所有这些重载而烦恼。始终按值获取std::wstring参数,并在 mem 初始值设定项中std::move它们。然后你只需要 3 个构造函数定义。需要注意的是,在传递右值的情况下,您会产生额外的移动构造,但您很可能会忍受这一点。

LogRecord(const Logger &logger, LogLevel level, std::wstring message)
    : level(level), logger_name(logger.GetName()), message(std::move(message)), ...
    {}

请注意,由于小字符串优化,对于 n 的小值,移动构造实际上可能是 O(n)。


另一种选择是评论中提到的完美转发。你可以做类似的事情

template<typename Message>
LogRecord(const Logger &logger, LogLevel level, Message&& message)
    : level(level), logger_name(logger.GetName()), message(std::forward<Message>(message)), ...
    {}

也许添加 static_assert s 以打印更好的错误消息,Message是或可转换为std::wstring

相关文章: