2016-08-28 25 views
20

otrzymuje następujące deklaracje:result_of dla obiektu zarejestrował się z CV kwalifikacje argumentem

struct MyClass { }; 
typedef int MyClass::*Mp; 

Zarówno gcc 6.2 i brzękiem kompilatora próbowałem, result_of<Mp(const MyClass)>::type daje int&&.

Podsumowanie moje pytanie: Dlaczego int&& i nie bądź const int&& lub po prostu int?

Więcej Tło: Norma mówi, że result_of jest zdefiniowana w ten sposób:

członek typu typedef wyznacza typ decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...));

Norma określa również powołać się na wskaźnik do członka -obiekty w ten sposób:

- t1. * f gdy N == 1, a f jest wskaźnikiem do elementu danych klasy T i is_base_of_v<T, decay_t<decltype(t1)>> jest prawdziwe;

Należy pamiętać, że decay_t służy wyłącznie do sprawdzania, czy dotyczy to tego wypunktowania. O ile mogę powiedzieć, stosując dwa punkty powyżej powinna przynieść:

decltype(declval<const MyClass>().*declval<Mp>()) 

co daje const int&&. Tak, brakuje mi czegoś lub są błędne biblioteki kompilatora?

Edit, 30 sierpnia 2016:

Dzięki za odpowiedzi. Kilka osób zaproponowało alternatywne sposoby uzyskania poprawnego wyniku bez używania result_of. Powinienem wyjaśnić, że powód dla którego jestem powiesił się na poprawnej definicji result_of jest to, że faktycznie wdrażam najbliższą rozsądną implementację result_of, która działa z kompilatorem przed C++ 11. Tak więc, podczas gdy zgadzam się, że mogę używać decltype lub result_of<Mp(const MyClass&&)>::type w C++ 11, nie robią tego, czego potrzebuję dla C++ 03. Kilka osób udzieliło poprawnej odpowiedzi, co oznacza, że ​​argumenty rvalue const dla funkcji nie są częścią typu funkcji. To wyjaśnia rzeczy dla mnie i będę implementował moje pre-C++ 11 result_of takie, że odrzuca te kwalifikatory.

+0

Um, myślę, że masz literówkę w deklaracji funkcji członka? Czy może czegoś brakuje? – Yakk

+0

Ithth__ czytam w książce _Stroustrup_, że 'const int &&' nie ma sensu (jako że cała idea wartości r jest taka, że ​​_ może być przenoszona, a 'const' blokuje to), więc' const' jest automatycznie usuwana. – Aganju

+0

@Yakk, nie ma deklaracji funkcji członka. Jest to deklaracja typu wskaźnik do elementu (dane), która jest używana. @Aganju, Stroustrup ma rację, że 'const int &&' ma ograniczoną użyteczność ALE to robi * ma sens *, jest zdefiniowane w języku i jest obsługiwane przez kompilatory. W rzeczywistości jego wygląd jest artefaktem konstruktów takich jak ten i nie widzę niczego w specyfikacji, które mogłoby temu zapobiec. (Jestem członkiem komitetu standardowego C++, więc mogę zadać to samo pytanie na standardowej liście e-mailowej.) –

Odpowiedz

8

const jest usuwany z parametrów funkcji. Możesz to sprawdzić, używając is_same.

void(int) == void(const int) 
Mp(MyClass) == Mp(const MyClass) 
result_of<Mp(MyClass)> == result_of<Mp(const MyClass)> 

myślę, że to jest wyjaśnione przez [8.3.5.5]:

Po wyprodukowaniu listę typów parametrów, wszelkie najwyższego poziomu CV-kwalifikacyjne modyfikujące typ parametru są usuwane podczas formowania typ funkcyjny. Wynikowa lista przekształconych typów parametrów i obecność lub nieobecność elipsy lub pakietu parametrów funkcji jest listą parametrów typu funkcji. [Uwaga: ta transformacja nie ma wpływu na typy parametrów. Na przykład int(*)(const int p, decltype(p)*) i int(*)(int, const int*) są identycznymi typami. - koniec uwaga]

można obejść przez Definiowanie własnego result_of że nie (mis) stosowanie typy funkcyjne:

template <typename F, typename... ArgTypes> 
struct my_result_of 
{ 
    using type = decltype(std::invoke(std::declval<F>(), std::declval<ArgTypes>()...)); 
}; 

Definicja ta jest naprawdę to, co średnia powinna być używana.

+0

Dziękuję za cytowanie sekcji standardu. Myślałem, że tak może być, ale niektóre eksperymenty sprawiały, że wątpiłem w siebie. Zastanawiam się, czy kompilatory faktycznie czasami zachowują kwalifikację CV na parametry funkcji i odrzucają je innym razem. Potrzebne są dalsze eksperymenty. –

+0

@PabloHalpern Biorąc pod uwagę 'void f (const int x) {}' typem 'f' jest' void (int) ', ale' x' jest zmienną 'const', która należy do body_ of 'f'. – Oktalist

+0

@PabloHalpern może myślisz o http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1322 i jego krewnych –

4

W result_of_t<Mp(const MyClass)> Ci wydają się być stara się zapytać jaki jest typ wyniku powołując Mp z const rvalue typu MyClass. Lepszym sposobem, aby zapytać, że z result_of będzie result_of_t<Mp(const MyClass&&)>, ale zwykle łatwiej jest po prostu użyć decltype i zapomnieć, że kiedykolwiek istniał result_of. Jeśli rzeczywiście zamierzałeś poprosić o wynik z wartością l const, byłby to result_of_t<Mp(const MyClass&)>.

To prawda, że ​​najwyższy poziom const dla parametrów funkcji nie ma znaczenia w deklaracji funkcji. Podczas korzystania z result_of, wydaje się, że bardziej sensowne jest dostarczanie typów argumentów jako odniesień do typów prawdopodobnie: const. Dzięki temu kategoria wartości jest wyraźna, bez utraty wyrazistości. Możemy użyć print_type sztuczki, aby zobaczyć co się dzieje, kiedy to zrobić:

template <typename...> struct print_type; // forward declaration 

print_type<std::result_of_t<Mp(const MyClass)>, 
      std::result_of_t<Mp(const MyClass&)>, 
      std::result_of_t<Mp(const MyClass&&)>, 
      std::result_of_t<Mp(MyClass)>, 
      std::result_of_t<Mp(MyClass&)>, 
      std::result_of_t<Mp(MyClass&&)>>{}; 

Drukuje:

error: invalid use of incomplete type 'struct print_type<int&&, const int&, const int&&, int&&, int&, int&&>' 

Można więc wywnioskować:

std::result_of_t<Mp(const MyClass)> == int&& 
std::result_of_t<Mp(const MyClass&)> == const int& 
std::result_of_t<Mp(const MyClass&&)> == const int&& 
std::result_of_t<Mp(MyClass)>   == int&& 
std::result_of_t<Mp(MyClass&)>  == int& 
std::result_of_t<Mp(MyClass&&)>  == int&& 

Możemy zobaczyć, że result_of_t<Mp(const MyClass)>, result_of_t<Mp(MyClass)> i result_of_t<Mp(MyClass&&)> wszystkie oznaczają to samo. Byłbym zaskoczony, gdyby tak się nie stało.

Należy pamiętać, że podczas korzystania z declval podajesz również typy argumentów jako referencje, ponieważ zadeklarowano, że declval zwraca referencję. Ponadto wszystkie parametry do std::invoke są odniesieniami.