如何最好地定义/构造/初始化std :: string包装类

问题描述

我在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_viewT可转换为字符串等);但是只接受原样并移动它会容易得多。这是最灵活的方法,同时又保持了简单性和可维护性。


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; 

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...