简体   繁体   English

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

[英]C++: Constructor accepting only a string literal

Is it possible to create a constructor (or function signature, for that matter) that only accepts a string literal, but not an eg char const * ? 是否可以创建接受字符串文字但不接受例如char const *的构造函数(或函数签名)?

Is it possible to have two overloads that can distinguish between string literals and char const * ? 是否可能有两个重载可以区分字符串文字和char const *

C++ 0x would kind-of allow this with a custom suffix - but I'm looking for an "earlier" solution. C ++ 0x将允许使用自定义后缀-但是我正在寻找“较早的”解决方案。

Rationale: avoiding heap copy of strings that won't be modified when given as string literals. 原理:避免以字符串文字形式给出时不会被修改的字符串的堆副本。

These strings directly go to an API expecting a const char * without any processing. 这些字符串无需经过任何处理即可直接转到需要const char *的API。 Most calls do use literals requiring no additional processing, only in a few cases they are constructed. 大多数调用确实使用不需要额外处理的文字,仅在少数情况下才构造它们。 I am looking for a possibility to preserve the native call behavior. 我正在寻找保留本地通话行为的可能性。

Note: - since it comes up in the answers: the code in question does not use std::string at all, but a good example would be: 注意: -因为它出现在答案中:有问题的代码根本不使用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; }
}

Results: 结果:

(1) it can't be done. (1)无法完成。
(2) I realized I am not even looking for a literal, but for a compile-time-constant (ie "anything that needs not be copied"). (2)我意识到我什至没有在寻找文字,而是在寻找编译时常数(即“不需要复制的任何内容”)。

I will probably use the following pattern instead: 我可能会改用以下模式:

const literal str_Ophelia = "Ophelia";

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

with a simple 一个简单的

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

That doesn't stop anyone from abusing it (I should find a better name...), but it allows the required optimization but remains safe by default. 这不会阻止任何人滥用它(我应该找到一个更好的名称...),但是它允许进行必要的优化,但默认情况下仍然安全。

Working solution based on sbi idea : 基于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
}

Yes, it can be done! 是的, 可以做到! I came up with a solution that works with C++03 and without a wrapper class (which breaks some implicit conversions in return statements). 我想出了一种可与C ++ 03一起使用且没有包装类的解决方案(该包装类会破坏return语句中的某些隐式转换)。

First of all, you need a constructor template for the types const char (&)[N] , since this is the original type of string literals. 首先,您需要类型const char (&)[N]的构造函数模板,因为这是字符串文字的原始类型。 Then you also need another one for the types char (&)[N] - like char buffers for instance - so that they con't end up in the constructor for literals. 然后,对于类型char (&)[N]还需要另一个类型char (&)[N]例如,像char缓冲区),以使它们不会出现在文字构造函数中。 And probably you also want a normal constructor for the type const char* . 可能还需要一个普通的const char*类型的构造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

The problem now is, that for string literals the compiler still thinks, that the const char* constructor is a better choice than a template instance, since array-to-pointer conversions have exact-match rank . 现在的问题是,对于字符串文字,编译器仍然认为, const char*构造函数是比模板实例更好的选择,因为数组到指针的转换具有精确匹配等级 (13.3.3.1.1) (13.3.3.1.1)

So, the trick is to lower the precedence of the const char* constructor. 因此,诀窍是降低const char*构造const char*的优先级。 This can be done by changing it to a template as well and using SFINAE to match it only against the type const char* . 也可以通过将其更改为模板并使用SFINAE使其仅与const char*类型进行匹配来实现。 The constructor has no return value and only one parameter, that is necessary for type deduction. 构造函数没有返回值,只有一个参数,这对于类型推导是必需的。 Therefore another "dummy parameter" with default value is necessary, that uses the conditional type trait: template<typename T> Foo(T, typename IsCharPtr<T>::Type=0) 因此,另一个具有默认值的“虚拟参数”是必需的,它使用条件类型特征: template<typename T> Foo(T, typename IsCharPtr<T>::Type=0)

Solution: 解:

#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;
}

No, you just can't do this - string literals and const char* are interchangeable. 不,您只是不能做到这一点-字符串文字和const char *是可互换的。 One workaround could be to introduce a special class to hold pointers to string literals and make a constructor only accepting that. 一种解决方法是引入一个特殊的类来保存指向字符串文字的指针,并使构造函数仅接受它。 This way whenever you need to pass a literal you call a constructor of that class and pass the temporary object. 这样,每当需要传递文字时,就调用该类的构造函数并传递临时对象。 This doesn't completely prevent misuse, but makes code much more maintainable. 这不能完全防止滥用,但是可以使代码更易于维护。

If you know exactly how your compiler and platform deal with string literals, it might be possible to write a solution that can do this. 如果您确切地知道编译器和平台如何处理字符串文字,则可以编写一个解决方案来解决此问题。 If you know that your compiler always puts string literals into a specific region of memory, you can check the pointer against the bounds of that memory. 如果知道编译器总是将字符串文字放入特定的内存区域,则可以对照该内存的边界检查指针。 If it falls within that block, you've got a string literal; 如果它位于该块之内,则说明您有字符串文字。 otherwise you've got a string stored on the heap or stack. 否则,您将有一个字符串存储在堆或堆栈中。

However, this solution would be platform/compiler-specific. 但是,此解决方案将特定于平台/编译器。 It would not be portable. 它不会是便携式的。

On some platforms, I have had to declare string literals as static const char * in order for the program to access the text from Read-Only Memory. 在某些平台上,我必须将字符串文字声明为static const char * ,以使程序可以从只读存储器中访问文本。 When declared as const char * , the assembly listing showed that the text was copied from ROM onto a stack variable. 当声明为const char * ,程序集清单显示该文本已从ROM复制到堆栈变量中。

Instead of worrying about the receiver, perhaps try declaring the string literals with static const char * . 不必担心接收方,可以尝试使用static const char *声明字符串文字。

With a new user-defined literals in C++14 (as for Clang 3.5 - it works with C++11 too), there is an elegant solution: 使用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);
}

It may be used like this: 可以这样使用:

void f1(Literal) {}  // Accepts literals only.

int main() {
  auto str1 = "OMG! Teh Rey!"_L;
  std::cout << str1 << std::endl;
  f(str1);
}

There is one drawback: you have to append _L to every literal - but it's not a big deal, really. 有一个缺点:您必须在每个文字后附加_L ,但这确实没什么大不了的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM