String literal reference class

In C++1y , can I have a reference class that binds to a string literal but not to char* or char[]& or similar?

 class LitRf{ const char* data_; size_t size_; public: LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ } }; 
+7
c ++ c ++ 14
source share
3 answers

I think it is best to use const char (&s)[N] (with template<size_t N> ) as the parameter type. But it also binds to any const char array other than a string literal.

Add the created array constructor without the char constructor to prevent it from being called using a non-constant array.

 class LitRf { const char* data_; Sz size_; public: template<size_t N> LitRf(char const (&s)[N]) : data_{s}, size_{N} {} template<size_t N> LitRf(char (&s)[N]) = delete; }; 

In addition, you can use the macro shell, which (when the constructor is never used without it) makes it possible only to create an object from a literal, even with a variable.

 #define MakeLitRf(s) LitRf(s "") 

The idea is to combine two string literals, the second of which is an empty string. This is only possible if the first is also a string literal; when setting a variable, a syntax error occurs. After macro expansion, the compiler sees something like LitRf("foo" "") , which is equivalent to LitRf("foo") . Some examples:

 auto x = MakeLitRf("foo"); // works const char *foo = "foo"; auto x = MakeLitRf(foo); // fails auto x = LitRf(foo); // works, but we want it to fail... 

In the latter case, the user inadvertently (or intentionally?) Did not use the macro, making our work useless. To make it crash, add a hidden parameter to the constructor, which you need to add when called directly (and in the macro definition, of course):

 class LitRf { const char* data_; Sz size_; public: // Called by the macro MakeLitRf. Unlikely to be called directly unless the user knows what he doing. LitRf(const char *s, void *) : data_{s}, size_{N} {} // Called without macro! Throw a compiler error, explaining what wrong. LitRf(const char *s) { static_assert(false, "Please use the macro `MakeLitRf` with a string literal to construct a `LitRf`."); } }; #define MakeLitRf(s) LitRf(s "", nullptr) 
+3
source share

C ++ 11 removed the only formal way to define a string literal as such, namely through its implicit conversion to a pointer to non-constant.

In any case, using a little trick, you had to use a macro.

In C ++ 11 and later, it is best to establish a strong convention that an array of const character type is a literal.

Ie, with your example,

 class LitRf { private: const char* data_; Sz size_; template< size_t n > LitRf( char (&)[n] ) = delete; public: template< size_t n > LitRf( char const (&s)[n] ) : data_{s}, size_{sizeof(s) - 1} {} }; 

Note the use of size_t instead of the possibly signed type Sz . This ensures that the code will be compiled with g ++. Unfortunately, this compiler or older versions have an error in which it is persistent on size_t , otherwise it refuses to accept the code.

+3
source share
 class LitRf{ const char* data_; Sz size_; public: LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ } LitRf(char*) = delete; }; 
+1
source share

All Articles