У меня есть функция, которая извлекает указатель на тип члена из других типов, и это работает:
template<class T2, class Array,
class Element = typename std::decay_t<Array>::element,
class PointerToMemberType = T2 Element::*
>
v f(Array&& a, PointerToMemberType pm);
Однако я не могу найти способ написать PointerToMemberType = ???
без предварительного определения Element
, которое можно опустить.
Как я могу написать PointerToMemberType
напрямую без использования вспомогательного Element
?
Я пробовал простую замену, но она не работает:
template<
class T2, class Array,
class PointerToMemberType = T2 typename std::decay_t<Array>::element::*
>
void f(Array&&, PointerToMemberType pm);
// error: expected identifier before ‘*’ token
677 | class PointerToMemberType = T2 typename std::decay_t<Array>::element::*
^
Я также пытался добавить typename
и круглые скобки в нескольких местах.
Обратите внимание, что PointerToMemberType
в настоящее время не используется для дедукции, хотя я постараюсь использовать его в будущем.
В некоторых местах рекомендуется использовать std::invoke
, чтобы не нужно было иметь дело с указателями на элементы данных. Как это подходит или упрощает что-то здесь?
Вспомогательная метафункция вполне бы справилась с задачей:
template <typename C, typename M>
using PM = M C::*;
template<
class T2, class Array,
class PointerToMemberType = PM<typename std::decay_t<Array>::element, T2>
>
...
Что касается того, можно ли это сделать «напрямую», ответ положительный, но вы должны пропускать ключевое слово typename
. Ваш компилятор должен принять это:
template<
class T2, class Array,
class PointerToMemberType = T2 std::decay_t<Array>::element::*
>
...
По стандарту [temp.res]/5:
A qualified name used as the name in a class-or-decltype (Clause 13) or an elaborated-type-specifier is implicitly assumed to name a type, without the use of the
typename
keyword. In a nested-name-specifier that immediately contains a nested-name-specifier that depends on a template parameter, the identifier or simple-template-id is implicitly assumed to name a type, without the use of thetypename
keyword. [ Note: Thetypename
keyword is not permitted by the syntax of these constructs. — end note ]
В нашей ситуации у нас есть спецификатор вложенного имениstd::decay_t<Array>::element::
, который сразу же содержит спецификатор вложенного имениstd::decay_t<Array>::
, который зависит от параметра шаблона, поэтому этот абзац говорит нам, что typename
не нужен. Ясно, что когда за std::decay_t<Array>::element
следует ::
, компилятор знает, что std::decay_t<Array>::element
— это тип, а не элемент данных или функция-член.
Согласно примечанию, грамматика запрещает ненужное использование typename
в этой ситуации. Правильное использование спецификатор имени типа в соответствии с [temp.res]/3:
typename
nested-name-specifieridentifier
Здесь typename
применяется к идентификатор после применения всего спецификатор вложенного имени, например, в typename A::B::C::D
оператор ::
связывает сильнее, чем typename
, поэтому вы говорите, что typename A::B::C::D
— это тип. спецификатор вложенного имени не может содержать typename
на верхнем уровне одного из своих компонентов, поскольку из [expr.prim.id.qual] крайний левый компонент может быть только имя типа, имя-пространства имен или спецификатор decltype и имя типа (в отличие от идентификатор типа). ) может быть только неполным именем или простой идентификатор шаблона. Не крайние левые компоненты могут быть только неполными именами или простой идентификатор шаблона (иногда требуется префикс template
).
Я обнаружил, что в некоторых случаях мне все равно нужно промежуточное определение, чтобы активировать Sfinae для встроенных типов и не получить серьезную ошибку.
Уфф, это я пропустил. На самом деле это не удалось в другом контексте, потому что он пытался создать экземпляр указателя на член встроенного типа, что невозможно.