2013-02-11 10 views
10

Poniższy kod kompiluje grzywny z gcc 4.7.2 (mingw)std :: unordered_map :: emplace problem z prywatnym/usunięty konstruktora kopii

#include <unordered_map> 
#include <tuple> 

struct test 
{ 
     test() =default; 
    private: 
     test(test const&) =delete; 
}; 

int main() 
{ 
    std::unordered_map<char, test> map; 

    map.emplace(
     std::piecewise_construct, 
     std::forward_as_tuple('a'), 
     std::forward_as_tuple() 
    ); 
} 

Gdybym jednak zmienić konstruktor kopiujący w test z test(test const&) =delete; do test(test const&) =default; , wymiata wymiaru szablonu wydaje się narzekać na to, że const test& nie może być zamiennikiem na test (tekst here). Czy nie powinienem pracować? A jeśli nie, czy obaj nie powinni popełnić błędu?

Odpowiedz

11

Jeśli spojrzeć na wymiocin błędów szablon dokładniej zobaczysz ten kawałek marchewki w nim:

test.exe.cpp:8:3: error: 'constexpr test::test(const test&)' is private 

To jest clue problemu.

GCC 4.7.2 nie sprawdza dostępu jako części odliczania argumentu szablonu (zgodnie z wymaganiami C++ 03). Cecha is_convertible jest zaimplementowana przy użyciu SFINAE, która polega na odjęciu argumentu w szablonie, i jeśli rozdzielczość przeciążenia wybiera deduplikację argumentu prywatnego konstruktora, ale wtedy kontrola dostępu nie powiedzie się, ponieważ wybrany konstruktor jest prywatny. Jest to problem z GCC 4.7, ponieważ nie został zmieniony zgodnie z nową regułą C++ 11 w 14.8.2 [temp.deduct], która mówi:

-8- Jeśli wynikiem zastąpienia jest nieprawidłowy typ lub wyrażenie, odliczanie typu nie powiedzie się. Nieprawidłowy typ lub wyrażenie to taki, który byłby źle sformułowany, gdyby został zapisany przy użyciu podstawionych argumentów. [Uwaga: Kontrola dostępu odbywa się w ramach procesu zastępowania. końcem uwaga]

Jest to ogromna zmiana dotychczasowych zasad odliczenia, że ​​pkt poprzednio powiedział

-8- Jeżeli podstawienie w nieprawidłowym typie lub wyrażenia typu odliczenie nie . Nieprawidłowy typ lub wyrażenie to taki, który byłby źle sformułowany, gdyby został zapisany przy użyciu podstawionych argumentów. Kontrola dostępu nie odbywa się w ramach procesu zastępowania. W konsekwencji, gdy dedukcja się powiedzie, błąd dostępu może nadal występować, gdy funkcja zostanie utworzona.

Zmiana powstał dość późno w procesie C++ 0x przez DR 1170 i sprawia SFINAE niesamowite w C++ 11 :)

GCC 4.8 wprowadza nowe zasady, więc is_convertible i podobnych cechach udzielić właściwej odpowiedzi dla niedostępnych konstruktorów.

4

Poprawna odpowiedź to Jonathan Wakeley. Zostawię to, ponieważ dostarcza użytecznych informacji dla osób, które mają podobny problem związany z insert.


Krótka wersja jest taka, że ​​jest to spowodowane problemem w realizacji Biblioteka standardowa używanego przez GCC 4.7.2, która wynikałaby z mylących sformułowań użytych w C++ 11 Standard. Istnieje propozycja zmiany brzmienia, a także poprawka do implementacji w GCC 4.8.


Długa wersja

This GCC bug entry zgłasza bardzo podobny problem, gdzie insert używany jest zamiast emplace. LibstdC++ realizacja insert zgodny ze standardowym, który stanowi o funkcji insert (konkretnie template <class P> pair<iterator,bool> insert(P&& obj)):

(§23.5.4.4/5) Uwagi: Podpis ten nie bierze udziału w rozdzielczości przeciążenia chyba P jest niejawnie zamienny do value_type.

libstdC++ wydaje się być realizowane przy użyciu tego wymogu oświadczenia enable_if który sprawdza std::is_convertible<> dla typów zaangażowanych.

Zgłoszony powyżej raport o błędach stwierdza, że ​​naprawdę należało użyć std::is_constructible<>, a sformułowanie w Standardzie powinno zostać zmienione. To linki do LWG (grupa robocza język) Problem, który proponuje już zmiany w standardowe dla tego (LWG issue #2005, zobacz Portland 2012 wpis, odpowiednią część proponowanej zmiany poniżej):

  1. Zmiana 23.5.4.4 [unord.map.modifers] wokół p. 1, jak wskazano:

    template <class P> 
    pair<iterator, bool> insert(P&& obj); 
    

[...] Uwagi: Podpis ten nie bierze udziału w rozdzielczości przeciążenia chyba P jest niejawnie zamienny do VALUE_TYPE std::is_constructible<value_type, P&&>::value jest prawdą.

Proponowana zmiana stanowi również, że działanie funkcji insert opisanej powyżej powinna być równoważna emplace(std::forward<P>(obj)). Dlatego prawdopodobnie można powiedzieć, że problem opisany w pytaniu jest dokładnie tym samym problemem.

I rzeczywiście, proponowane zmiany wydają się być odzwierciedlone w ostatnich GCC 4.8 migawek: Podczas kompilowania kodu z GCC 4.8, kontrola is_convertible nie jest wykonywane i nie pojawia komunikat o błędzie.

+1

Blisko, ale nie cygar :) LWG 2005 odnosi się tylko do '' emplace' insert' nie, powód program kończy się niepowodzeniem z GCC 4.7.2 jest to, że nie robi kontroli dostępu w ramach odliczenia szablon argument (jak wymagane w C++ 03), więc 'is_constructible' otrzymuje błąd dostępu z powodu prywatnego konstruktora. GCC 4.8 implementuje reguły C++ 11 i sprawdza dostęp podczas dedukcji argumentu szablonu –

+2

Możesz potwierdzić, że różnica nie wynika z jakiejkolwiek zmiany w standardowej bibliotece przez wstępne przetworzenie kodu przy pomocy G ++ 4.7 (więc używa biblioteki z 4.7), a następnie kompilacja to z 4.8, w którym to przypadku program działa, udowadniając, że to nie zmieniona biblioteka, a nie biblioteka –

Powiązane problemy