views:

575

answers:

4

Is it possible to create a constructor (or function signature, for that matter) that only accepts a string literal, but not an e.g. char const *?

Is it possible to have two overloads that can distinguish between string literals and char const *?

C++ 0x would kind-of allow this with a custom suffix - but I'm looking for an "earlier" solution.

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. 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:

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.
(2) I realized I am not even looking for a literal, but for a compile-time-constant (i.e. "anything that needs not be copied").

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.

+10  A: 

No, you just can't do this - string literals and const char* are interchangeable. 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.

sharptooth
+3  A: 

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.

Ant
probably the only "safe" way to do that, but I don't want to rely on that much unportable trickery. Anyway, thanks!
peterchen
+6  A: 

Working solution based on 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
}
Kirill V. Lyadvinsky
@Downvoter: Care to comment? Which bit do you disagree with?
Kirill V. Lyadvinsky
Probably the fact that it doesn't work - it recognises character arrays as string literals for a start.
Joe Gauterin
nonetheless, goo idea for working around the overload resolution problem.
peterchen
@Joe, now it recognizes character arrays as pointers.
Kirill V. Lyadvinsky
+1: Its not possible to differentiate between "const char []" and a string literal, so this is the optimal solution.
Richard Corden
In C++03 you can keep them apart by checking whether they convert to both `char*` and `const char[]`: If they do, it's a string literal, if they don't, it's a plain array. But in C++0x, they removed the deprecated conversion from a string literal to `char*`, thus this hack won't work anymore then :)
Johannes Schaub - litb
@Johannes: That's a use of that particular conversion I'm sure nobody ever envisaged!
Richard Corden
A: 

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. When declared as const char *, the assembly listing showed that the text was copied from ROM onto a stack variable.

Instead of worrying about the receiver, perhaps try declaring the string literals with static const char *.

Thomas Matthews