代码之家  ›  专栏  ›  技术社区  ›  peterchen

C++:仅接受字符串文字的构造函数

  •  19
  • peterchen  · 技术社区  · 15 年前

    是否可以创建一个 只有 接受字符串文字,但不接受。 char const * ?

    是否可以有两个重载来区分字符串和文本 字符常数* ?

    C++ 0x会允许使用自定义后缀——但是我正在寻找一个“早期”的解决方案。

    理论基础:

    这些字符串直接发送到需要 const char *

    -因为它出现在答案中:所讨论的代码不使用 std::string 一个很好的例子是:

    class foo
    {
       std::string m_str;
       char const * m_cstr;      
     public:
       foo(<string literal> s) : m_cstr(p) {}
       foo(char const * s) : m_str(s) { m_cstr = s.c_str(); }
       foo(std::string const & s) : m_str(s) { m_cstr = s.c_str(); }
    
       operator char const *() const { return m_cstr; }
    }
    

    (1) 这是办不到的。
    (2) 我意识到我甚至不是在寻找一个文字,而是一个编译时常量(即“任何不需要复制的东西”)。

    我可能会使用以下模式:

    const literal str_Ophelia = "Ophelia";
    
    void Foo()
    {
      Hamlet(str_Ophelia, ...);  // can receive literal or string or const char *
    }
    

    struct literal  
    { 
       char const * data; 
       literal(char const * p) : data(p) {} 
       operator const char *() const { return data; }
    };
    

    这并不能阻止任何人滥用它(我应该找一个更好的名字…),但它允许进行必要的优化,但默认情况下仍然是安全的。

    6 回复  |  直到 15 年前
        1
  •  9
  •   sharptooth    15 年前

    不,你不能这么做-字符串文字和常量字符*是可以互换的。一种解决方法是引入一个特殊类来保存指向字符串文本的指针,并使构造函数只接受该指针。这样,每当需要传递文本时,就调用该类的构造函数并传递临时对象。这并不能完全防止误用,但会使代码更易于维护。

        2
  •  20
  •   Community c0D3l0g1c    7 年前

    基于 sbi idea :

    struct char_wrapper
    {
        char_wrapper(const char* val) : val(val) {};
        const char* val;
    };
    
    class MyClass {
    public:
      template< std::size_t N >
      explicit MyClass(const char (&str)[N])
      {
          cout << "LITERAL" << endl;
      }
      template< std::size_t N >
      explicit MyClass(char (&str)[N])
      {
          cout << "pointer" << endl;
      }    
      MyClass(char_wrapper m)
      {
         cout << "pointer" << endl;
      }
    };
    
    int main()
    {
        MyClass z("TEST1");     // LITERAL
        const char* b = "fff";
        MyClass a(b);           // pointer
        char tmp[256]; 
        strcpy(tmp, "hello"); 
        MyClass c(tmp);         // pointer
    }
    
        3
  •  19
  •   ansiwen    10 年前

    可以 完了!我提出了一个使用C++03的解决方案,但没有包装器类(它破坏了返回语句中的一些隐式转换)。

    const char (&)[N] ,因为这是字符串文字的原始类型。然后,您还需要另一个类型 char (&)[N] -例如,与字符缓冲区类似,这样它们就不会在文本的构造函数中结束。您可能还需要该类型的普通构造函数 const char* .

    template<int N> Foo(const char (&)[N]); // for string literals
    template<int N> Foo(char (&)[N]);       // for non-const char arrays like buffers
    Foo(const char*);                       // normal c strings
    

    现在的问题是,对于字符串文本,编译器仍然认为 常量字符* 数组到指针的转换 . (13.3.3.1.1)

    所以,诀窍是降低 常量字符* 常量字符* template<typename T> Foo(T, typename IsCharPtr<T>::Type=0)

    解决方案:

    #include <iostream>
    
    #define BARK std::cout << __PRETTY_FUNCTION__ << std::endl
    
    struct Dummy {};
    template<typename T> struct IsCharPtr {};
    template<> struct IsCharPtr<const char *> { typedef Dummy* Type; };
    template<> struct IsCharPtr<char *> { typedef Dummy* Type; };
    
    struct Foo {
      template<int N> Foo(const char (&)[N]) { BARK; }
      template<int N> Foo(char (&)[N]) { BARK; }
      template<typename T> Foo(T, typename IsCharPtr<T>::Type=0) { BARK; }
    };
    
    const char a[] = "x";
    const char* b = "x";
    const char* f() { return b; }
    
    int main() {
      char buffer[10] = "lkj";
      char* c = buffer;
      Foo l("x");     // Foo::Foo(const char (&)[N]) [N = 2]
      Foo aa(a);      // Foo::Foo(const char (&)[N]) [N = 2]
      Foo bb(b);      // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = const char *]
      Foo cc(c);      // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = char *]
      Foo ee(buffer); // Foo::Foo(char (&)[N]) [N = 10]
      Foo ff(f());    // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = const char *]
      return 0;
    }
    
        4
  •  3
  •   Ant    15 年前

    如果您确切地知道编译器和平台如何处理字符串文本,那么就有可能编写一个解决方案来实现这一点。如果您知道编译器总是将字符串文本放入内存的特定区域,则可以对照该内存的边界检查指针。如果它在那个块中,你就得到了一个字符串文本;否则,您将在堆或堆栈上存储一个字符串。

    但是,此解决方案是特定于平台/编译器的。它不会是便携式的。

        5
  •  1
  •   Thomas Matthews    15 年前

    在某些平台上,我不得不将字符串文本声明为 static const char * const char * ,程序集列表显示文本是从ROM复制到堆栈变量上的。

    与其担心接收者,不如尝试用 .

        6
  •  1
  •   abyss.7    10 年前

    有了C++14中新的用户定义的文本(与Clang 3.5一样,它也适用于C++11),就有了一个优雅的解决方案:

    class Literal {
     public:
      explicit Literal(const char* literal) : literal_(literal) {}
      // The constructor is public to allow explicit conversion of external string
      // literals to `_L` literals. If there is no such need, then move constructor
      // to private section.
    
      operator const char* () { return literal_; }
    
     private:
      friend Literal operator"" _L (const char*, unsigned long);
      // Helps, when constructor is moved to private section.
    
      const char* literal_;
    };
    
    Literal operator"" _L (const char* str, unsigned long) {
      return Literal(str);
    }
    

    void f1(Literal) {}  // Accepts literals only.
    
    int main() {
      auto str1 = "OMG! Teh Rey!"_L;
      std::cout << str1 << std::endl;
      f(str1);
    }
    

    有一个缺点:您必须附加 _L 从字面上看,这并不是什么大问题,真的。