问题描述
我在std :: string周围使用包装器类,但是初始化/构造它的最简单/最干净/最完整的方法是什么。我至少需要3种方式
- 来自字符串文字
- 从std :: string右值开始,避免复制!?
- 从string_view和string(是,复制)
天真的程序员只想将任何构造自动委托给std :: string,但这不是一个功能。
struct SimpleString
{
SimpleString() = default;
template<typename T>
SimpleString( T t ) : Text( t ) { } // <==== experimental
// Alternative: are these OK
SimpleString( const char* text ) : Text( text ) { }
SimpleString( std::string&& text ) : Text( text ) { }
SimpleString( const std::string_view text ) : Text( text ) { }
std::string Text;
};
优先注释:是的,我想要它,我需要它。用例:调用一个通用函数,在该函数中,对SimpleString和std :: string的处理不同。
关于从std :: string继承的注意事项:可能是个坏主意,因为隐式转换将在第一次机会发生。
解决方法
如果您的主要目标是用于字符串转换构造函数,则无需经历任何麻烦。最简单,最好的方法是仅接受API上的std::string
附加值并将其std::move
固定就位。
class SimpleString
{
public:
SimpleString() = default;
SimpleString(std::string text) : Text(std::move(text)) { }
// ... other constructors / assignment ...
private:
std::string Text;
};
这样做,我们可以利用std::string
已经可以通过以下方式构造的事实:
- C字符串/文字
-
std::string_view
, - 其他
std::string
个对象(左值或右值),和 - 任何可能已转换为
std::string
的用户定义类型
执行此操作还使用户可以选择现在通过移动其当前SimpleString
对象还是允许std::string
显式复制对象来决定是否要构建SimpleString
。这为呼叫者提供了更大的灵活性。
这很好,因为您已经已经拥有了这个std::string
,因此按值接受它并std::move
处理对象是“容纳”该值的一种简单方法。移动std::string
相当便宜,总共需要分配一些指针,大小和分配器-因此产生的性能开销可以忽略不计,同时也是一种简单且可维护的方法。
现代C ++和移动语义使接受这些类型变得非常容易。您肯定会可以重载所有N种构造方法({const char*
,std::string_view
,T
可转换为字符串等);但是只接受原样并移动它会容易得多。这是最灵活的方法,同时又保持了简单性和可维护性。
Here are some benchmarks来比较使用模板与使用std::string
+ std::move
。通常,保持简单更为有利。您的同事和未来的维护者将感谢您(即使那个未来的维护者是您,一年后)。
std::string
有很多构造函数,因此必须在类中重新实现所有构造函数会很麻烦。这就是为什么您最适合像这样转发到#include <utility>
struct SimpleString
{
template <typename... Args>
SimpleString(Args &&... args) : Text(std::forward<Args>(args)...)
{
}
std::string Text;
};
的构造函数的原因:
template <typename... Args>
constexpr bool is_empty_pack_v = sizeof...(Args) == 0;
template <typename T,typename... Args>
constexpr bool is_copy_or_move_pack_v = sizeof...(Args) == 1 &&
(... && std::is_same_v<T,std::remove_reference_t<Args>>);
struct SimpleString
{
SimpleString() = default;
SimpleString(const SimpleString &) = default;
SimpleString(SimpleString &&) = default;
template <typename... Args,std::enable_if_t<
not is_empty_pack_v<Args...> &&
not is_copy_or_move_pack_v<SimpleString,Args...> &&
std::is_constructible<std::string,Args...>,int> = 0>
SimpleString(Args &&... args) : Text(std::forward<Args>(args)...)
{
}
std::string Text;
};
为避免与默认构造函数和复制/移动构造函数发生冲突,我们将需要使用SFINAE:
write_transaction := TFDTransaction.Create;
write_connection := TFDConnection.Create;
write_transaction.Connection := write_connection;
write_connection.StartTransaction;