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

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

我希望能够使用 move semantics 或复制语义来初始化 class 的每个字段。构造函数将使用本质上相同的代码进行构造,如下所示:

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是成员的class,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-initializer 中获取 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_asserts 以打印更好的错误消息 Message 是,或可转换为 std::wstring