#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;
}
@user17732522 user17732522 Если я расширим T U::*
типами void()
и Foo
, получится void() Foo::*
, что не имеет никакого смысла, не могли бы вы уточнить, как синтаксис T U::*
действителен?
@Elliott Тип возвращаемого значения функции Foo::bar
— void
, но T
это не void
T
на самом деле void()
.
@JamesAdkison Псевдонимы типов, как правило, нельзя просто заменить текстом. Это то же самое для обычных указателей функций, например. учитывая using T = void();
, тип T*
является указателем на void()
-функцию, хотя синтаксис для этого без псевдонима будет void(*)()
. Дополнительные модификаторы должны применяться семантически к типу с псевдонимом.
@ user17732522 Я не знаком с псевдонимами типов. У вас есть хорошие ссылки, которые могут предоставить более подробную информацию?
Я оставляю свой комментарий как ссылку на бога: godbolt.org/z/Prbca5PYK Хотя @user17732522, вероятно, ответил на него (как только я понимаю, что они сказали!)
@JamesAdkison, псевдонимы типов буквально просто using T = int
или (идентично) typedef int T
.
Просто T U::*
означает «указатель на член класса U с типом T», а здесь T
— это void()
. То, что синтаксис объявления без T
был бы void (U::*)()
не имеет значения.
@ user17732522, о. Таким образом, void()
на самом деле представляет собой тип функции без аргументов, которая возвращает void. Честно говоря, я думал, что это трактует void
как значение, например, int()
для int x = int();
.
@Elliott void()
может быть типом функции без параметров и возвращаемого типа или может быть выражением типа void
. Учитывая, что мы говорили о типах, я предположил, что понятно, что имеется в виду предыдущая интерпретация.
struct Trait<T U::*> ^^^^^^ // I don't understand this syntax
Приведенный выше синтаксис означает, что у нас есть указатель на член класса с именем U
, где член имеет тип T
. Другими словами, указатель на член класса U
, который имеет тип T
.
Теперь давайте применим это к &Foo::bar
. Тип выражения &Foo::bar
:
void (Foo::* f)()
Теперь вы можете сравнить это с T U::*
Итак, после сравнения получаем:
U = Foo
который является тип класса.T = void()
что означает функцию, которая имеет тип возвращаемого значения void
и не принимает параметров, т. е. тип функции.что фактически означает, что у нас есть указатель на функцию-член, которая имеет тип возвращаемого значения void
и не принимает параметров класса Foo
.
Потому что
Foo::bar
имеет типvoid()
?