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

C++ 如何最好地定义/构造/初始化std::string包装类,c++,string,wrapper,C++,String,Wrapper,我在std::string周围使用一个包装器类,但是初始化/构造它的最简单/最干净/最完整的方法是什么。我至少需要3种方法 从字符串文本 从std::字符串右值,避免复制!? 从string_视图和string yes中,复制 天真的程序员只想自动将任何构造委托给std::string,但这不是一个特性 struct SimpleString { SimpleString() = default; template<typename T> Simpl

我在std::string周围使用一个包装器类,但是初始化/构造它的最简单/最干净/最完整的方法是什么。我至少需要3种方法

从字符串文本 从std::字符串右值,避免复制!? 从string_视图和string yes中,复制 天真的程序员只想自动将任何构造委托给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 by value并将其移动到位

类简单限制 { 公众: SimpleString=默认值; SimpleStringstd::字符串文本:Textstd::movetext{} //…其他构造函数/任务。。。 私人: std::字符串文本; }; 这样,我们可以利用std::string已经可以通过以下方式构造的事实:

C字符串/文字, std::string_视图, 其他std::string对象(左值或右值),以及 任何可能已转换为std::string的用户定义类型 这样做还让用户现在可以选择是移动当前std::string对象还是允许SimpleString显式复制它来构造SimpleString。这为调用者提供了更大的灵活性

这很好,因为您无论如何都将拥有这个std::字符串,因此通过值和std::移动对象来接受它是放置这个值的一种简单方法。移动一个std::string是相当便宜的,相当于为指针、大小和分配器分配了几个赋值——因此这产生的性能开销可以忽略不计,同时也是一种简单且可维护的方法

现代C++和移动语义使它很容易接受这些类型。您肯定可以去重载所有N种构造它的方法,包括const char*、std::string_view、可转换为字符串的Ts等等;但只接受类型并移动它会简单得多。这是最灵活的方法,同时也保持了它的简单性和可维护性

比较使用模板与使用std::string+std::move。总的来说,保持简单更有利。一年后,你的同事和未来的维护者会感谢你,即使未来的维护者是你。

std::string有很多构造函数,在你的类中重新实现它们会很麻烦。这就是为什么您最适合转发到std::string的构造函数,如下所示:

包括 结构简单限制 { 样板 SimpleStringArgs&…args:Textstd::forwardargs。。。 { } std::字符串文本; }; 为了避免与默认构造函数和复制/移动构造函数冲突,我们需要使用SFINAE:

样板 constexpr bool为空\u pack\u v=sizeof…Args==0; 样板 constexpr bool是\u copy\u或\u move\u pack\u v=sizeof…Args==1&& ... && 标准::是否相同; 结构简单限制 { SimpleString=默认值; SimpleStringconst SimpleString&=默认值; SimpleStringSimpleString&&=默认值; 模板=0> SimpleStringArgs&…args:Textstd::forwardargs。。。 { } std::字符串文本; };
为什么需要std::string包装器?用例是什么?它应该提供哪些std::string没有的特性?如果您只需要向它添加一点功能,最好是继承它。@J.Schultke您读过preemptive note吗?看,我会立即使用派生类变体,但我认为无论出于什么原因都不赞成它。如果我的类只是一个不添加任何内容的包装器,那就应该可以了!?如果我想添加一个标志怎么办?啊,皱眉的原因是隐式转换为STD::String,我需要避免。Josuttis有一个非常有趣的谈话,讨论了类似的情况和这里的问题:现代C++和移动语义-你是指遗留C++移动语义?关于标准图书馆的争论是没有意义的。它处理未知类型,并且无法决定调用此类型的move构造函数是否是一个几乎没有操作的操作。正因为如此,它往往不得不走得更远。在本例中,我们知道类型,并且知道std::string的move构造函数非常便宜。最后,但不是列表,在标准库中,您将永远不会发现至少我没有见过像这样贪婪的模板构造函数,正是因为我们试图解释的原因。我添加了。这两种解决方案在实践中产生了几乎相同的字符串构造性能。@J.Schultke OP没有
t声明他需要所有底层构造函数都是可调用的。by value解决方案允许用户简单地调用std::string{n,c}作为SimpleStringWrapper的参数,它仍然具有完全相同的性能特征——这比复杂的模板更易于维护,而不是破坏。这根本没有道理;如果不构造对象,就不能破坏它。基准链接记录循环中任何内容的时间,并按比例进行测试。由于DonoOptimize,结果未优化。你给我看的是组装,不是基准测试。你应该在Google Bench上阅读——这是一个学习真正基准测试的非常酷的工具;这段对话已经结束。