2012-12-30 13 views
10

Poniższy kod nie kompiluje w GCC 4.7.2 lub Clang 3.2:Nie można skopiować std :: wektor <std :: function <void()>> za pomocą jednolitej inicjalizacji. Czy to jest poprawne?

#include <vector> 
#include <functional> 

int main() 
{ 
    std::vector<std::function<void()>> a; 
    std::vector<std::function<void()>> b{a}; 
} 

Problem jest, że kompilator będzie próbował tworzyć b stosując initializer_list, gdy wyraźnie powinno być po prostu wywołanie konstruktora kopii. Jednak wydaje się, że to pożądane zachowanie, ponieważ standard mówi, że konstruktory initializer_list powinny mieć pierwszeństwo.

Kod ten działałby dobrze dla innych std :: vector, ale dla funkcji std :: kompilator nie może wiedzieć, czy chcesz konstruktor initializer_list czy inny.

Nie wygląda na to, że istnieje sposób obejścia tego, a jeśli tak jest, to nigdy nie można użyć jednolitej inicjalizacji w kodzie szablonowym. Co byłoby wielkim wstydem.

Z drugiej strony Visual Studio (2012 CTP w listopadzie) nie narzeka na to. Jednak obsługa initializer_list nie jest w tym momencie zbyt dobra, więc może to być błąd.

Odpowiedz

10

To jest LWG 2132, który nie jest jeszcze raportem o usterkach, ale istnieje wyraźna zgoda (i doświadczenie wdrożeniowe), aby to naprawić. Standard mówi, że konstruktor std::function zaakceptuje dowolny typ, więc ponieważ konstruktor listy inicjalizacyjnej jest zawsze preferowany względem innych konstruktorów, jeśli jest to wykonalne, twój kod próbuje zbudować wektor z obiektu std::initializer_list<std::function<void()>> z pojedynczym elementem zainicjowanym z obiektu a. To powoduje błąd, ponieważ pomimo tego, że można skonstruować obiekt std::function<void()> z a, wynikowy obiekt nie jest możliwy do wywołania.

Innymi słowy, problem polega na tym, że std::function ma nieograniczony konstruktor szablonu, który umożliwia konwersję z dowolnego typu. To powoduje problem w twoim przypadku, ponieważ konstruktory listy inicjalizacyjnej są preferowane w stosunku do innych konstruktorów, jeśli są wykonalne, a konstruktor nieograniczony function oznacza, że ​​zawsze można utworzyć initializer_list<function<void()>> z dowolnego typu, aby konstruktor listy inicjalizującej był zawsze wykonalny.

Proponowana rezolucja do 2132 uniemożliwia skonstruowanie std::function z typu niewymienialnego, więc konstruktor listy inicjalizacyjnej nie jest opłacalny, a zamiast tego wywoływany jest konstruktor kopii vector. I implemented that resolution for GCC 4.8, i to jest już zaimplementowane w bibliotece libC++ Clanga.

+0

Dzięki za odpowiedź. Łącze LWG 2132 tak naprawdę mówi o problemie, który jest podobny, ale inny, że tak właśnie jest, aby rozwiązać ten problem. Oznacza to, że problem zostanie rozwiązany tylko dla std :: function, a nie dla innych typów z szablonowymi konstruktorami. Myślę, że moja nowa reguła dla jednolitej inicjalizacji to "użyj jej wszędzie, gdzie możesz, chyba że obiekt ma konstruktor initializer_list." Używaj go tylko, jeśli chcesz tego konstruktora."Co oznacza także, że nie można go używać w szablonowym kodzie." –

+0

Wolę regułę "nie pisz nieograniczonych szablonów konstruktorów, które umożliwiają niejawną konwersję z dowolnego typu." Jeśli nie tworzysz takich typów, to wygrywasz " • powodują problemy dla osób, które próbują użyć twojego typu z klasami, które mają konstruktory listy inicjalizacyjnej. Standardowa biblioteka nie przestrzegała tej reguły, ale jest to naprawione dla 'std :: function'. –

5

Nie widzę żadnego powodu, dla którego to nie powinno się skompilować i zarówno gcc (wersja 4.8.0 20121111) jak i clang (wersja 3.3 (trunk 171007)) kompilują kod. To powiedziawszy, "jednolita inicjalizacja" jest daleka od jednolitości: Zdecydowanie są przypadki, w których nie można używać nawiasów klamrowych podczas wywoływania konstruktora.

+0

g ++ 4.7.2 ('4.7.2-5ubuntu1') nie kompiluje kodu. Bardzo dziwny komunikat o błędzie kompilatora: http://pastebin.com/b1mcbYRq – leemes

+1

@ od czasu, gdy pytanie otwiera się z "Poniższy kod nie kompiluje się w GCC 4.7.2 lub Clang 3.2:", zakładam, że OP wie o tym (i prawdopodobnie jest to powód pytania w pierwszej kolejności). – WhozCraig

+0

@WhozCraig tak, ale nie dostarczył komunikatu kompilatora. Właśnie dlatego opublikowałem ten komentarz w pierwszej kolejności. – leemes

Powiązane problemy