20

是否可以创建一个接受字符串文字但不接受 eg的构造函数(或函数签名) char const *

是否有可能有两个可以区分字符串文字和的重载char const *

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

基本原理:避免作为字符串文字给出时不会被修改的字符串的堆副本。

这些字符串直接发送到 API,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; }
};

这并不能阻止任何人滥用它(我应该找到一个更好的名字......),但它允许所需的优化,但默认情况下保持安全。

4

6 回答 6

20

基于sbi 想法的工作解决方案:

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
}
于 2010-01-11T12:19:28.870 回答
19

是的,可以做到!我想出了一个适用于 C++03 并且没有包装类的解决方案(它破坏了返回语句中的一些隐式转换)。

首先,您需要 types 的构造函数模板const char (&)[N],因为这是字符串文字的原始类型。然后,您还需要另一个类型的类型char (&)[N]- 例如 char 缓冲区 - 以便它们不会最终出现在文字的构造函数中。并且可能您还需要一个普通的 type 构造函数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

现在的问题是,对于字符串文字,编译器仍然认为const char*构造函数是比模板实例更好的选择,因为数组到指针的转换具有精确匹配等级。(13.3.3.1.1)

所以,诀窍是降低const char*构造函数的优先级。这也可以通过将其更改为模板并使用 SFINAE 将其仅与 type 匹配来完成const char*。构造函数没有返回值,只有一个参数,这是类型推导所必需的。因此,需要另一个具有默认值的“虚拟参数”,它使用条件类型特征: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;
}
于 2014-06-19T00:18:52.903 回答
9

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

于 2010-01-11T11:06:57.093 回答
3

如果您确切地知道您的编译器和平台如何处理字符串文字,则可以编写一个可以做到这一点的解决方案。如果您知道您的编译器总是将字符串文字放入特定的内存区域,您可以根据该内存的边界检查指针。如果它属于该块,则您有一个字符串文字;否则你有一个字符串存储在堆或堆栈上。

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

于 2010-01-11T11:43:56.633 回答
1

在某些平台上,我必须声明字符串文字static const char *,以便程序从只读内存中访问文本。当声明为const char *时,汇编列表显示该文本已从 ROM 复制到堆栈变量中。

与其担心接收者,不如尝试用static const char *.

于 2010-01-11T17:54:24.717 回答
1

使用 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到每一个文字上——但这并不是什么大问题,真的。

于 2014-06-13T08:49:04.007 回答