2009-10-12 14 views
9

Szukam łatwego sposobu na zbudowanie tablicy łańcuchów w czasie kompilacji. Dla testu ułożyła klasę o nazwie Strings który ma następujące osoby:Nadużywanie operatora przecinka

Strings(); 
Strings(const Strings& that); 
Strings(const char* s1); 
Strings& operator=(const char* s1); 
Strings& operator,(const char* s2); 

Korzystanie z tego, mogę z powodzeniem kompilacji kodu tak:

Strings s; 
s="Hello","World!"; 

s="Hello" część wywołuje operator= która zwraca a Strings&, a następnie operator, zostaje wywołana dla "World!".

Nie mogę dostać się do pracy (w MSVC, nie próbowałem jeszcze innych kompilatorów) jest

Strings s="Hello","World!"; 

bym Zakładamy tutaj, że Strings s="Hello" by wywołać konstruktor kopiujący i wtedy wszystko będzie zachowywać taki sam jak pierwszy przykład. Ale pojawia się błąd: error C2059: syntax error : 'string'

Jednak to działa prawidłowo:

Strings s="Hello"; 

Więc wiem, że konstruktor kopia robi przynajmniej pracę na jednej strunie. Jakieś pomysły? Naprawdę chciałbym, aby druga metoda działała tylko po to, aby kod był trochę czystszy.

+8

Wow, słyszałem dużo dowcipów na temat przeciążania operatora przecinków. Nigdy się nie spodziewałem, że ktoś naprawdę to zrobi. –

+2

@Carl: A co z http://www.boost.org/doc/libs/1_40_0/libs/assign/doc/index.html#intro (bardzo lubię to, co OP chce) – UncleBens

+0

* dreszcz * Własne paznokcie trumna. Jak zamierzasz to debugować, gdy zaczyna się źle? –

Odpowiedz

14

Myślę, że przecinek w twoim drugim przykładzie nie jest operatorem przecinka, ale raczej elementem gramatyki dla wielu deklaracji zmiennych.

przykład, ten sam sposób, że można napisać:

int a=3, b=4 

Wydaje mi się, że są w zasadzie pisząc:

Strings s="Hello", stringliteral 

więc kompilator spodziewa pozycję po przecinku być nazwa zmiennej, a zamiast tego widzi literał ciągu i ogłasza błąd. Innymi słowy, konstruktor jest stosowany do "Hello", ale przecinek to , a nie operator przecinków ciągów.

Nawiasem mówiąc, konstruktor tak naprawdę nie jest konstruktorem kopiowania. Tworzy obiekt Strings z literalnego parametru łańcuchowego ... Termin konstruktor kopiowania jest zwykle stosowany do tego samego typu.

+2

Ryzyko bycia nużąco pedantycznym, 'Strings s =" Hello ";' jest równoważne 'Strings s = Strings (" Hello ");'. Wymaga to, aby ctor kopia była zdolna do wywoływania, a może nawet wywoływała to, aby skonstruować 's' z tymczasowego obiektu. Ale optymalizacja, aby zastąpić ją bezpośrednią inicjalizacją za pomocą konstruktora 'const char *' dla 's', bez żadnych tymczasowych, jest dozwolona i powszechna. –

+0

Duh, nie mogę uwierzyć, że tego nie widziałem. – miked

8

Nie polecam tego rodzaju API. Będziesz nadal odkrywał przypadki, które nie działają zgodnie z oczekiwaniami, ponieważ przecinek jest operatorem o najniższym priorytecie.Na przykład, w tym przypadku nie będzie działać albo:

if ("Hello","world" == otherStrings) { ... } 

Możesz być w stanie uzyskać rzeczy pracy, jeśli używasz nawiasów za każdym razem całego zestawu strun, tak:

Strings s=("Hello","World!"); 

i My powyższy przykład wyglądałby następująco:

if (("Hello","world") == otherStrings) { ... } 

to może być dokonane częściej do pracy, ale składnia skróconą nie jest chyba warta Tricky semantyka, które pochodzą z nim.

+3

Czy to może zadziałać? Problem, który przewiduję, polega na tym, że nie można przeciążać 'operatora,' dla 'const char *', więc '(" Hello "," world ")' jest takie samo jak '" world "'. –

+0

Założono, że można przeciążać operatora, (const char *, const char *). Czy jest jakiś powód, dla którego nie można tego zrobić? Nie robię zbyt wiele programowania w C++, szczerze mówiąc. –

0

Można użyć tablicę wskaźników znaków

Strings::Strings(const char* input[]); 

const char* input[] = { 
    "string one", 
    "string two", 
    0}; 

Strings s(input); 

i wewnątrz konstruktora, iterację wskaźników aż trafisz null.

+0

Lub szablon konstruktora: 'template Ciągi znaków (const char * (& input) [N]) {for (int i = 0; i

+0

Oh, lub oczywiście szablon konstruktora może wywoływać inną funkcję podobną do 'vector :: insert (iterator, InputIterator, InputIterator)', aby uniknąć duplikowania generowanego kodu, jeśli wywołasz go z wieloma różnymi długościami w różnych miejscach. –

0

Jeśli C++ 0x, mają dla tego nowy inializer lists! Chciałbym móc z nich skorzystać. Na przykład:

std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" }; 
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" }; 
0

Jeśli jedynym zadaniem Strings jest przechowywanie listy ciągów, następnie boost::assign mógł wykonać zadanie lepiej w standardowych pojemnikach, myślę :)

using namespace boost::assign; 
vector<string> listOfThings; 
listOfThings += "Hello", "World!"; 
1

jest to możliwe, aby ta praca, dla wystarczająco luźnej definicji "pracy". Oto działający przykład, który napisałem w odpowiedzi na podobne pytanie kilka lat temu. To było zabawne wyzwanie, ale nie użyłbym go w prawdziwym kodzie:

#include <iostream> 
#include <algorithm> 
#include <iterator> 
#include <vector> 

void f0(std::vector<int> const &v) { 
    std::copy(v.begin(), v.end(), 
     std::ostream_iterator<int>(std::cout, "\t")); 
    std::cout << "\n"; 
} 

template<class T> 
class make_vector { 
    std::vector<T> data; 
public: 
    make_vector(T const &val) { 
     data.push_back(val); 
    } 

    make_vector<T> &operator,(T const &t) { 
     data.push_back(t); 
     return *this; 
    } 

    operator std::vector<T>() { return data; } 
}; 

template<class T> 
make_vector<T> makeVect(T const &t) { 
    return make_vector<T>(t); 
} 

int main() { 
    f0((makeVect(1), 2, 3, 4, 5)); 
    f0((makeVect(1), 2, 3)); 
    return 0; 
} 
Powiązane problemy