Каков этот синтаксис указателя члена?

#include <iostream>
#include <type_traits>

struct Foo
{
    // ### Member Function ###
    void bar() { std::cout << "Foo::bar()\n"; }
};

// ### 1 Parameter Primary Template (which is empty) ###
template<typename>
struct Traits {};

// ### 2 Parameter Template Specialization ###
template<class T, class U>
struct Traits<T U::*>
              ^^^^^^ // I don't understand this syntax
{
    using type1 = T;
    using type2 = U;
};

int main()
{
    // ### Pointer to member function ###
    void (Foo::*memFuncPtr)() = &Foo::bar;
    
    Foo f;

    // ### Use the member function pointer to invoke it ###
    (f.*memFuncPtr)();
    
    static_assert(std::is_same_v<void(), Traits<decltype(&Foo::bar)>::type1>);
    static_assert(std::is_same_v<Foo, Traits<decltype(&Foo::bar)>::type2>);

    return 0;
}

Как работает синтаксис специализации? Имеет смысл, что U в U::* совпадает с типом Foo, но почему T совпадает с типом void()?

Редактировать

После очень полезных комментариев от @user17732522 и ответа @AnoopRana я смог изменить реализацию функции main, чтобы использовать тот же синтаксис (просто чтобы увидеть, как это работает).

int main()
{
    using F = void();

    // ### Pointer to member function ###
    F Foo::*memFuncPtr = &Foo::bar;

    Foo f;

    // ### Use the member function pointer to invoke it ###
    (f.*memFuncPtr)();

    static_assert(std::is_same_v<void(), Traits<decltype(&Foo::bar)>::type1>);
    static_assert(std::is_same_v<Foo, Traits<decltype(&Foo::bar)>::type2>);

    return 0;
}

Потому что Foo::bar имеет тип void()?

user17732522 09.04.2022 06:34

@user17732522 user17732522 Если я расширим T U::* типами void() и Foo, получится void() Foo::*, что не имеет никакого смысла, не могли бы вы уточнить, как синтаксис T U::* действителен?

James Adkison 09.04.2022 06:36

@Elliott Тип возвращаемого значения функции Foo::barvoid, но Tэто не voidT на самом деле void().

James Adkison 09.04.2022 06:37

@JamesAdkison Псевдонимы типов, как правило, нельзя просто заменить текстом. Это то же самое для обычных указателей функций, например. учитывая using T = void();, тип T* является указателем на void()-функцию, хотя синтаксис для этого без псевдонима будет void(*)(). Дополнительные модификаторы должны применяться семантически к типу с псевдонимом.

user17732522 09.04.2022 06:41

@ user17732522 Я не знаком с псевдонимами типов. У вас есть хорошие ссылки, которые могут предоставить более подробную информацию?

James Adkison 09.04.2022 06:43

Я оставляю свой комментарий как ссылку на бога: godbolt.org/z/Prbca5PYK Хотя @user17732522, вероятно, ответил на него (как только я понимаю, что они сказали!)

Elliott 09.04.2022 06:44

@JamesAdkison, псевдонимы типов буквально просто using T = int или (идентично) typedef int T.

Elliott 09.04.2022 06:46

Просто T U::* означает «указатель на член класса U с типом T», а здесь T — это void(). То, что синтаксис объявления без T был бы void (U::*)() не имеет значения.

user17732522 09.04.2022 06:47

@ user17732522, о. Таким образом, void() на самом деле представляет собой тип функции без аргументов, которая возвращает void. Честно говоря, я думал, что это трактует void как значение, например, int() для int x = int();.

Elliott 09.04.2022 06:52

@Elliott void() может быть типом функции без параметров и возвращаемого типа или может быть выражением типа void. Учитывая, что мы говорили о типах, я предположил, что понятно, что имеется в виду предыдущая интерпретация.

user17732522 09.04.2022 06:55
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
5
10
94
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
struct Trait<T U::*>
             ^^^^^^ // I don't understand this syntax

Приведенный выше синтаксис означает, что у нас есть указатель на член класса с именем U, где член имеет тип T. Другими словами, указатель на член класса U, который имеет тип T.

Теперь давайте применим это к &Foo::bar. Тип выражения &Foo::bar:

void (Foo::* f)()

Теперь вы можете сравнить это с T U::*

Итак, после сравнения получаем:

  1. U = Foo который является тип класса.
  2. T = void() что означает функцию, которая имеет тип возвращаемого значения void и не принимает параметров, т. е. тип функции.

что фактически означает, что у нас есть указатель на функцию-член, которая имеет тип возвращаемого значения void и не принимает параметров класса Foo.

Другие вопросы по теме