C++ std::string和string文本之间不一致

C++ std::string和string文本之间不一致,c++,string,foreach,c++11,string-literals,C++,String,Foreach,C++11,String Literals,我发现C++0x中的std::string和字符串文本之间存在令人不安的不一致性: #include <iostream> #include <string> int main() { int i = 0; for (auto e : "hello") ++i; std::cout << "Number of elements: " << i << '\n'; i = 0; f

我发现C++0x中的
std::string
和字符串文本之间存在令人不安的不一致性:

#include <iostream>
#include <string>

int main()
{
    int i = 0;
    for (auto e : "hello")
        ++i;
    std::cout << "Number of elements: " << i << '\n';

    i = 0;
    for (auto e : std::string("hello"))
        ++i;
    std::cout << "Number of elements: " << i << '\n';

    return 0;
}
我理解发生这种情况的机制:字符串文字实际上是一个包含空字符的字符数组,当基于范围的for循环调用字符数组上的
std::end()
时,它会得到一个指针,指针超过数组的末尾;由于空字符是数组的一部分,因此它将获得一个指针,该指针将超过空字符

但是,我认为这是非常不可取的:当涉及到与长度一样基本的属性时,
std::string
和字符串文本的行为应该是相同的吗

有没有办法解决这种不一致性?例如,可以为字符数组重载
std::begin()
std::end()
,以便它们所限定的范围不包括终止的空字符吗?如果是,为什么不这样做

<强>编辑< /强>:为那些愤愤不平的人提供更多的理由,我说我正承受使用C风格字符串“遗留特征”的后果,请考虑如下代码:

template <typename Range>
void f(Range&& r)
{
    for (auto e : r)
    {
        ...
    }
}
模板
无效f(范围和r)
{
用于(自动e:r)
{
...
}
}
您希望
f(“hello”)
f(std::string(“hello”)
做些不同的事情吗

但是,我认为这是非常不可取的:当属性的基本属性与其长度相同时,std::string和string文本的行为应该是相同的吗

根据定义,字符串文字在字符串末尾有一个(隐藏的)空字符。Std::字符串不支持。因为std::strings有一个长度,所以空字符有点多余。字符串库上的标准部分明确允许以非null结尾的字符串

编辑
我认为我从未给出过一个更具争议性的答案,即大量的赞成票和大量的反对票

auto
迭代器应用于C样式数组时,会在数组的每个元素上进行迭代。范围的确定是在编译时进行的,而不是在运行时。这是格式错误的,例如:

char * str;
for (auto c : str) {
   do_something_with (c);
}
有些人使用char类型的数组来保存任意数据。是的,这是一种老式的C思维方式,也许他们应该使用C++风格的std::array,但是这种构造非常有效,非常有用。如果他们的自动迭代器在
char缓冲区[1024]上运行,这些人会非常不安在元素15处停止,只是因为该元素恰好与空字符具有相同的值。
类型缓冲区[1024]上的自动迭代器将一直运行到最后。是什么让char数组值得采用完全不同的实现

请注意,如果希望字符数组上的自动迭代器提前停止,有一种简单的机制可以做到这一点:添加
if(c=='0')break语句添加到循环体


一句话:这里没有不一致之处。Cux[]数组上的<代码> Auth/Ung>迭代器与AutoTyror如何工作任何其他C样式数组一致。

< P>如果您想要长度,则应该使用C++代码字符串< <代码> >代码> > C++字符串>你不能对C字符串和C++字符串进行相同的处理——它们有不同的行为。

如果重载<代码>:STD::/Cord>和<代码> STD::/CONT> const char数组返回一个小于数组大小的代码,那么下面的代码将输出4而不是预期的5:

#include <iostream>

int main()
{
    const char s[5] = {'h', 'e', 'l', 'l', 'o'};
    int i = 0;
    for (auto e : s)
        ++i;
    std::cout << "Number of elements: " << i << '\n';
}
#包括
int main()
{
常量字符s[5]={'h','e','l','l','o'};
int i=0;
用于(自动e:s)
++一,;

Std::CUT> P>,在第一种情况下,你得到< C++ > 6代码,是一个抽象的漏洞,在C.<代码> STD::String “修复”中是不可避免的。为了兼容性,C风格字符串文字的行为在C++中没有改变。 例如,std::begin()和std::end()是否可以重载 字符数组,以便它们所限定的范围不包括 终止空字符?如果是,为什么不这样做

假设通过指针访问(与
char[N]
相反),只需在包含字符数的字符串中嵌入一个变量,这样就不再需要搜索
NULL
。哎呀!这就是
std::string


“解决不一致性”的方法是根本不使用传统功能。

根据N3290 6.5.4,如果范围是数组,则边界值为 自动初始化,无需
开始
/
结束
功能分派。
那么,像下面这样准备一些包装怎么样

struct literal_t {
    char const *b, *e;
    literal_t( char const* b, char const* e ) : b( b ), e( e ) {}
    char const* begin() const { return b; }
    char const* end  () const { return e; }
};

template< int N >
literal_t literal( char const (&a)[N] ) {
    return literal_t( a, a + N - 1 );
};
如果您的编译器提供用户定义的文字,则缩写以下内容可能会有所帮助:

literal operator"" _l( char const* p, std::size_t l ) {
    return literal_t( p, p + l ); // l excludes '\0'
}

for (auto e : "hello"_l) ...
编辑:以下内容的开销较小 (不过,用户定义的文本将不可用)

模板
字符常量(&literal(字符常量(&x)[N]))[N-1]{
返回(字符常量(&)[N-1])x;
}
对于(自动e:literal(“hello”))。。。

可以使用C++0x工具箱中的另一个工具:用户定义的文本来解决不一致性。使用适当定义的用户定义的文本:

std::string operator""s(const char* p, size_t n)
{
    return string(p, n);
}
我们将能够写:

int i = 0;     
for (auto e : "hello"s)         
    ++i;     
std::cout << "Number of elements: " << i << '\n';

有了这些新的std::string文本,可以说再也没有理由使用C样式的字符串文本了。

也许有一种方法可以区分定义为字符串文本的字符数组和通常定义的字符数组?我们只想重载前者。我不知道在库中有什么方法可以做到这一点。您必须这样做进行语言更改,该更改将破坏代码。窄字符串文字被定义为n个常量字符的数组,其中n是字符数加上一个终止null。任何解决方案都需要解决如何处理
const字符s[6]={h',e',l',l',o','\0'};template< size_t N >
char const (&literal( char const (&x)[ N ] ))[ N - 1 ] {
    return (char const(&)[ N - 1 ]) x;
}

for (auto e : literal("hello")) ...
std::string operator""s(const char* p, size_t n)
{
    return string(p, n);
}
int i = 0;     
for (auto e : "hello"s)         
    ++i;     
std::cout << "Number of elements: " << i << '\n';
Number of elements: 5