2013-06-20 12 views
8

Czy jest możliwe aby napisać klasy takie, że są to ważne:Czy mogę przeciążyć domyślną inicjalizację na 0?

Foo a; 
Foo b = 0; 
Foo c = b; 
Foo d(0); 
Foo e(1); 
Foo f = Foo(1); 

Ale nie są to:

int x; 
Foo a = x; 
Foo b = 1; 
Foo c = 2; 
//etc 

Zasadniczo, moja zasada jest „Stała 0 jest niejawnie przekształcić w Foo , ale żadna inna wartość nie jest "

+0

Co z 'Foo e (1);'? Jak to działa? –

+0

@LuchianGrigore: Czy są semantycznie identyczne? Jeśli tak, domyślam się, że mogę to unieważnić. – Eric

+1

Być może możesz spróbować z konstruktorem biorąc 'std :: nullptr_t' (tylko pomysł ...) –

Odpowiedz

5

Jeśli nie przeszkadza Ci praca Foo b = nullptr;, łatwo zhakować. Miej wyraźny konstruktor od int i domyślny od std::nullptr_t.

Jeśli masz coś przeciwko temu, nie jestem pewien, czy to możliwe. Jedynym sposobem na rozróżnienie literału literowego 0 i innych literałów całkowitych jest jego niejawna konwersja na wskaźniki i nullptr_t. Tak więc nullptr woli parametr nullptr_t od parametru wskaźnika, więc mając oba konstruktory można odfiltrować argumenty nullptr. Jednak konwersje 0 na wskaźniki i nullptr_t mają tę samą wartość, więc zabiłoby to argumenty o niejednoznaczności.

Hmm ... coś jak to może działać:

class Foo { 
    struct dummy; 
public: 
    explicit Foo(int); // the version that allows Foo x(1); 
    Foo(dummy*); // the version that allows Foo x = 0; 
    template <typename T, 
      typename = typename std::enable_if< 
       std::is_same<T, std::nullptr_t>::value>::type> 
    Foo(T) = delete; // the version that prevents Foo x = nullptr; 
}; 

ja nie faktycznie próbowałem. Teoretycznie szablon powinien brać udział tylko w rozwiązywaniu przeciążenia, gdy argumentem jest nullptr, ponieważ w przeciwnym razie SFINAE go zabije. W takim przypadku powinien jednak być lepszy niż konstruktor wskaźnika.

0
Foo e(1); 

Czy ll nie jawny konstruktor Foo przyjmujący argument jako argument. Zasadniczo ta linia zrobi to samo, próbując przekonwertować int na Foo przy użyciu tego konstruktora int.

Foo b = 1; 

Nie można zapobiec bezpośredniemu przetwarzaniu niektórych wartości tego int. Jeśli masz swojego konstruktora explicit, nie będziesz mógł napisać następnego wiersza.

Foo b = 0; 

gx_ poprawnie stwierdził, że 0 można przekonwertować na std :: nullptr_t. Poniższe czynności będą działać w odniesieniu do Twojego zamiaru.

Foo(std::nullptr_t x) : member(0) { } 
explicit Foo(int c) : member(c) { } 
// ... 
Foo a = 0; // compiles 
Foo b = 1; // doesn't compile 

// Note: 
int do_stuff (void) { return 0; } 
Foo c = do_stuff(); // doesn't compile 
+0

Nie, direct- vs. copy - inicjalizacja. –

+1

Co powiesz na 'wyraźne Foo (int);' plus 'Foo (nullptr_t);'? –

+0

+1, Raczej myślenie boczne, ale możliwe – Pixelchemist

-1

Jeden pomysł miałam:

Foo(const uint32_t c) : member(0) { static_assert(c == 0, "Nope"); } 
explicit Foo(uint32_t c) : member(c) { } 

Czy to zachowują się rozsądnie?

+2

To nawet nie skompiluje. Jeśli chodzi o rozdzielczość przeciążania, parametr 'const uint32_t' i parametr' uint32_t' to to samo. –

-1

muszę przyznać, że nie osiągnęliśmy całkowite opanowanie rvalue semantyki jeszcze C++ 11, ale to wydaje się robić to, co chcesz:

class Foo 
{ 
    public: 
    Foo(int&&) {} 
}; 

int main() 
{ 
    Foo a(123); 
    int x = 123; 
    Foo b(x); // error here, line 11 
    return 0; 
} 

Wynik:

prog.cpp:11: error: cannot bind ‘int’ lvalue to ‘int&&’

Komentarze witam, jeśli ten kod ma jakiekolwiek zastrzeżenia, których nie zauważyłem, lub jeśli możesz mnie uspokoić, że tak nie jest.

+0

Interesujące, ale staram się o konkretny przypadek 'x == 0'; – Eric

+0

@eric Ah, nie zdawałem sobie z tego sprawy. Powiedziałeś "Foo e (1);" powinno być prawidłowe: / – Oktalist

Powiązane problemy