2016-03-03 2 views
7

В C++1y, могу ли я иметь ссылочный класс, который связывается с строковым литералом, но не с char* или char[]& или аналогичным?Строковый литерал ссылочный класс

class LitRf{ 
    const char* data_; 
    size_t size_; 
public: 
    LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ } 
}; 
+0

Лучшее, что я могу придумать, чтобы обеспечить шаблонную перегрузку, которую статические_ассылки, или использовать явное ключевое слово. – Borgleader

+0

@Borgleader Но я не думаю, что существует способ различать строковый литерал и массив const char. – HolyBlackCat

+0

@Borgleader Как бы вы 'static_assert' что-то было строковым литералом? И как здесь может помочь «явный»? Фактически, если вы можете 'static_assert', что что-то является строковым литералом, вы также можете использовать то же условие с SFINAE для управления разрешением перегрузки, чтобы не привязываться к функции в первую очередь. – 5gon12eder

ответ

3

Я думаю, что лучше всего использовать const char (&s)[N]template<size_t N>) в качестве типа параметра. Но он также связывается с любым массивом const char, отличным от строкового литерала.

Добавить удаленный конструктор массива без const char, чтобы запретить вызов его с помощью неконстантного массива.

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

Кроме этого, вы можете использовать макро-обертку, который (если конструктор никогда не используется без него) делает возможным только построить объект из буквальных, даже через переменный.

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

Идея состоит в том, чтобы объединить две строки литералов, из которых вторая одна является просто пустая строка. Это возможно только в том случае, если первый также является строковым литералом; при установке переменной возникает синтаксическая ошибка. После макрорасширения компилятор видит что-то вроде LitRf("foo" ""), что эквивалентно LitRf("foo"). Некоторые примеры:

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

В последнем случае пользователь случайно (? Или намеренно) не использовать макрос, чтобы сделать нашу работу бесполезной. Для того, чтобы сделать это не в состоянии тоже добавить скрытый параметр в конструктор, который необходимо добавить, когда вызывается непосредственно (и в определении макроса, конечно):

class LitRf 
{ 
    const char* data_; 
    Sz size_; 
public: 
    // Called by the macro MakeLitRf. Unlikely to be called directly unless the user knows what he's doing. 
    LitRf(const char *s, void *) 
     : data_{s}, size_{N} 
    {} 

    // Called without macro! Throw a compiler error, explaining what's 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) 
1
class LitRf{ 
    const char* data_; 
    Sz size_; 
public: 
    LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ } 
    LitRf(char*) = delete; 
}; 
3

C++ 11 удален единственный формальный способ обнаружения строкового литерала как таковые, а именно через ее неявное преобразование в указатель на неконстантный.

В любом случае, используя небольшой трюк, нужно было использовать макрос.

В C++ 11 и более поздних версиях вы можете установить сильное соглашение о том, что массив const тип символа - это буквальный.

т.е. с вашим примером,

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

Обратите внимание на использование size_t вместо возможно знакового типа Sz. Это гарантирует, что код будет скомпилирован с g ++. К сожалению, у этого компилятора или более старых версий есть ошибка, где он настойчив на size_t, иначе он отказывается принять код.

+0

Спасибо. 'char const []' ~ 'строковый литерал' является разумным компромиссом. К сожалению, это также будет инициализироваться из массива non-const char. (Не обращайте внимания на 'Sz'. Я не знал, что это что-то - я использую его как' typedef' для 'std :: size_t' в моем пространстве имен) – PSkocik

+0

Но вы не можете различать массив' const char' и 'const' массива' char' – Barry

+0

Почему 'sizeof (s) - 1'? Я знаю, завершение 0, но ОП не вычитал это изначально. – leemes

Смежные вопросы