2016-03-03 14 views
7

Czy w C++1y mogę mieć klasę odniesienia, która wiąże się z literałem tekstowym, ale nie z char* lub char[]& lub podobnym?String reference reference class

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

Najlepsze co mogę wymyślić to dostarczyć szablonowe przeciążenie, które static_asserts, lub użyj jawnego słowa kluczowego. – Borgleader

+0

@Borgleader Ale nie sądzę, istnieje sposób, aby odróżnić ciąg literal od stałej tablicy char. – HolyBlackCat

+0

@Borgleader W jaki sposób "static_assert" powiedziałbyś, że coś jest literałem ciągu? A w jaki sposób "explicite" może tu pomóc? Właściwie, jeśli możesz 'static_assert', że coś jest literałem ciągu, możesz również użyć tego samego warunku z SFINAE, aby kontrolować rozdzielczość przeciążania, aby nie wiązać się z funkcją w pierwszej kolejności. – 5gon12eder

Odpowiedz

3

Domyślam się, że najlepiej jest użyć const char (&s)[N] (z template<size_t N>) jako typ parametru. Ale wiąże się również z dowolną tablicą znaków stałych inną niż literał łańcuchowy.

Dodanie konstruktora niewymiennej tablicy char, aby zabronić wywoływania jej za pomocą tablicy niezmiennej.

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

Poza tym, można użyć makro owijkę, która (gdy konstruktor nigdy nie jest używany bez niego) pozwala jedynie na budowę obiektu z dosłownym, nawet za pomocą zmiennej.

Chodzi o połączenie dwóch ciągów literowych, z których drugi to pusty ciąg. Jest to możliwe tylko wtedy, gdy pierwszy jest również literałem ciągu; wstawienie zmiennej powoduje błąd składni. Po rozwinięciu makr kompilator widzi coś w rodzaju LitRf("foo" ""), co jest równoważne z LitRf("foo"). Kilka przykładów:

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

W tym ostatnim przypadku, użytkownik niechcący (? Lub celowo) nie korzystać z makr, dzięki czemu nasza praca bezwartościowe. Aby sprawić, żeby się również zawiedził, dodaj do konstruktora ukryty parametr, który jest wymagany do dodania, gdy zostanie wywołany bezpośrednio (i oczywiście w definicji makra):

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 usunięto jedynie formalny sposób wykryć ciąg dosłowny jako takie, a mianowicie poprzez jego niejawna konwersja do wskaźnika do const.

W każdym razie, używając małej sztuczki, trzeba było użyć makra.

w C++ 11, a później najlepiej można zrobić jest stworzenie silnej konwencję że tablicą typu const znaków jest dosłownym.

To znaczy, ze swoim przykładzie,

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

Uwaga wykorzystanie size_t zamiast ewentualnie podpisanym typu Sz. Zapewni to, że kod skompiluje się z g ++. Niestety ten kompilator lub jego starsze wersje mają błąd, który jest bardzo natarczywy na size_t, inaczej odmawia akceptacji kodu.

+0

Dzięki. 'char const []' ~ 'literal literowy' jest rozsądnym kompromisem. Niestety, będzie to również inicjowane z niezawartej tablicy char. (Nieważne, że "Sz" .Nie wiedziałem, że to cokolwiek - używam go jako 'typedef' dla' std :: size_t' w mojej przestrzeni nazw) – PSkocik

+0

Ale nie możesz odróżnić tablicy 'const char' i 'const' tablica' char' – Barry

+0

Dlaczego 'sizeof (s) - 1'? Wiem, kończące 0, ale OP nie odjęło tego pierwotnie. – leemes