c ++中字符串(不是std :: string)常量的正确习惯用法

时间:2010-10-21 21:44:25

标签: c++ string

前段时间我问过std :: string常量correct idiom for std::string constants?

我从中得到的是使用std :: string常量但使用char字符串常量。那么什么是最好的习语

#define FOO "foo"

const char * const FOO = "foo";

const char FOO[] = "foo";

理想的功能

  • 在编译时获取长度。 1& 3但是 不是2(sizeof不适用于2)
  • 可以包含在没有链接器的.h中 抱怨。所有(我认为)
  • 在.o中没有多个副本,在链接中 输出。取决于编译器 (可能)

所以似乎#3最好,但scott meyers说使用#2(有效的c ++项目#1)

答案摘要

  1. 使用快乐复杂的模板代码
  2. 使用#3
  3. 模板代码感觉有点矫枉过正。所以现在我选择#3;

    但我会反思模板代码,宏观版本让它看起来很好看;但我不喜欢它不便携的事实(谁知道,也许gcc也会决定它的错误)

5 个答案:

答案 0 :(得分:4)

对于您想要的功能,......

  • 在编译时获取长度,
  • 可以包含在.h中,而没有链接器抱怨所有,
  • 在.o中没有多个副本,在链接的输出中,

...您可以使用模板常量惯用语,例如

template< class Dummy >
struct Foo_
{
    static char const s[];
};

template< class Dummy >
char const Foo_<Dummy>::s[] = "Blah blah";

typedef Foo_<void> Foo;    // Now you can refer to Foo:s


#include <iostream>
using namespace std;
int main()
{
    cout << sizeof( Foo::s ) << " bytes: \"" << Foo::s << "\"\n";
}

您可以将生成包装在宏中。

但是,据我所知,唯一可行的实用工具是支持char / wchar_t-agnostic代码,因此痛苦可能大于增益。

修改
错误的MSVC版本7.1到10.0不正确不接受sizeof。以下是与g ++ 4.4.1,Comeau Online 4.3.10.1,MSVC 7.1和M​​SVC 10.0编译良好的解决方法。

#include <stddef.h>

typedef ptrdiff_t   Size;

// Substitute a more general countOf
template< Size n >
struct SizedBuf { char sizer[n]; };

template< class Type, Size n >
SizedBuf< n > countOf_( Type (&)[n] ) { return n; }

#define COUNT_OF( array ) sizeof( countOf_( array ).sizer )

#define DEF_STRING( name, value )                               \
    template< class >                                           \
    struct name##_constant_                                     \
    {                                                           \
        static char const str[];                                \
        static Size const length    = COUNT_OF( value ) - 1;    \
    };                                                          \
                                                                \
    template< class Type >                                      \
    char const name##_constant_< Type >::str[] = value;         \
                                                                \
    template< class Type >                                      \
    Size const name##_constant_< Type >::length;                \
                                                                \
    typedef name##_constant_<void>  name;


DEF_STRING( a, "Argh, MSVC!" )
DEF_STRING( b, "Blah blah" )
DEF_STRING( c, "Currently there's no 'inline' for data in C++." )


#include <iostream>

template< char const* s >
void foo() { std::cout << "foo() says: " << s << std::endl; }

int main()
{
    using namespace std;

    int const x[a::length] = {};    // Showing off compile time constant.
    foo<a::str>();                  // Showing off external linkage.

    cout << a::length << " characters: \"" << a::str << "\"." << endl;
}

干杯&amp;第h。,

答案 1 :(得分:0)

您想要的功能是矛盾的。

  1. 编译时的长度
  2. 在头文件中定义
  3. 跨编译单元的单一副本
  4. 要获取(1)和(2),您需要将变量声明为static或将其放在未命名的命名空间中。但是,这将导致每个编译单元中的定义,这与(3)相反。

    要得到(2)和(3),你需要将变量声明为extern,但是你不会得到(1)。

    现在,如果您的链接器是智能的,它可能会优化多个副本,但我不确定该标准是否允许它...

    我建议在未命名的命名空间中声明const char FOO[] = "foo";语法,如果需要在特定的命名空间中找到static。如果字符串非常大,那么我选择extern

答案 2 :(得分:0)

这就是我的看法。我不会使用任何这些。首先,我倾向于#2,但是,考虑到你必须在.h中将变量声明为extern,并选择一些.cpp来实际存储字符串:

// .h
extern const char*const STR;

// .cpp
const  char*const STR = "abc";

在运行时没有长度的唯一缺点似乎不是我转向其他选项的真正原因。如果你有几个字符串,你总是可以有一组整数常量(或#define s)来指定每个字符串的长度,例如STR_LEN。如果你有很多它们,你无论如何也不会把它们写在手边,然后你可以同时自动生成..._LEN常数。

答案 3 :(得分:0)

我认为你从错误的想法中删除了以前的问题 字符串往往是稀疏的,不适合查找,而是使用数字。

在.hpp中声明存储和在.cpp中定义存储之间似乎没有区别,因此避免了多个副本。即使您有多个副本(具有不同的常量名称),您仍然会遇到上一个问题中提到的问题。

  

有一个致命的缺陷。我不能拥有使用这些字符串的静态模块级代码,因为它们可能尚未构建

避免这种情况的唯一方法是将常量与当前静态模块级别的范围放在同一范围内。

将所有相关内容放在课堂上!

答案 4 :(得分:0)

这只是Alf的答案:

#include <iostream>

#define string_constant(pName, pLiteral)                    \
        template <typename = void>                          \
        struct pName##_detail                               \
        {                                                   \
            static const char value[];                      \
        };                                                  \
                                                            \
        template <typename T>                               \
        const char pName##_detail<T>::value[] = pLiteral;   \
                                                            \
        typedef pName##_detail<> pName

string_constant(my_constant, "this is a literal");

int main()
{
    std::cout << my_constant::value << std::endl;
    std::cout << sizeof my_constant::value << std::endl;
}

codepad。似乎在VS2010中不起作用。 :/