Является ли имя нестатического члена зависимым при использовании в нестатической функции-члене?
Оба gcc 5.0 и clang 3.6 требуют ключевое слово typename
в следующем примере:
template<int n>
struct I
{
typedef int Type;
};
template<typename T>
struct A
{
int m;
void f()
{
typedef typename I<sizeof m>::Type Type; // typename required
}
};
Это соответствует следующей формулировке в стандарте С++ 11:
[temp.dep.type]/8
Тип зависит, если он
- простой шаблон-идентификатор, в котором либо имя шаблона является параметром шаблона, либо любым шаблоном аргументы - зависимый тип или выражение, зависящее от типа или зависящее от стоимости
Так что I<sizeof m>
зависит, если sizeof m
зависит от значения.
[temp.dep.expr]/4
Выражения следующих форм никогда не зависят от типа (поскольку тип выражения не может быть зависимый):
sizeof unary-expression
[temp.dep.constexpr]/2
Выражения следующей формы зависят от стоимости, если унитарное выражение или выражение являются независимыми или идентификатор типа зависит:
sizeof unary-expression
Итак, sizeof m
зависит только от m
.
[expr.prim.general]/8
Внутри определение нестатической функции-члена, идентификатор, который называет нестатический элемент, преобразуется в выражение доступа к члену класса
So m
является членом в выражении доступа к члену класса.
[temp.dep.type]/4
Имя является членом текущего экземпляра, если оно
- Идентификатор, обозначающий член в выражении доступа к члену класса (5.2.5), для которого тип выражения объекта является текущим экземпляром и id-выражением при поиске (3.4.5), относится, по меньшей мере, к одному члену текущего экземпляра или его независимого базового класса.
Итак, кажется, что m
является членом текущего экземпляра.
[temp.dep.type]/5
Имя является членом неизвестной специализации, если оно
Итак, m
НЕ является членом неизвестной специализации - он будет найден по имени, чтобы быть членом текущего экземпляра.
[temp.dep.expr]/3
Идентификатор id зависит от типа, если он содержит
- идентификатор, связанный с поиском имени с одной или несколькими объявлениями, объявленными с зависимым типом,
- спецификатор вложенного имени или квалифицированный идентификатор, который называет члена неизвестной специализации
Так как m
имеет тип int
и не является членом неизвестной специализации, ни одна из этих маркеров не сделает id-выражение m
зависимым.
[temp.dep.expr]/5
Выражение доступа к члену класса (5.2.5) зависит от типа, если выражение относится к члену текущего экземпляр и тип ссылочного элемента зависят или выражение доступа к члену класса относится к члену неизвестной специализации.
Когда m
преобразуется в выражение доступа к члену класса, оно все еще не зависит от него, поскольку оно не относится к члену неизвестной специализации.
Должен ли m
считаться зависимым? В отношении соответствующей заметки, следует ли this->m
считать зависимым? Что насчет std::declval<A>().m
?
ИЗМЕНИТЬ
И, наконец, должен ли &A::m
быть зависимым?
Ответы
Ответ 1
Как правильно сказано вами, sizeof m
преобразуется в sizeof (*this).m
.
sizeof
зависит только в том случае, если выражение аргумента зависит от типа, чего нет, согласно [temp.dep.expr]/5:
Выражение доступа к члену класса (5.2.5) зависит от типа, если выражение относится к члену текущего экземпляра и тип ссылочного элемента зависит или доступ к члену класса выражение относится к члену неизвестной специализации.
Тип m
не зависит, и выражение не относится к члену неизвестной специализации - [temp.dep.type]/6:
Имя является членом неизвестной специализации, если оно
- Идентификатор, обозначающий член в выражении доступа к члену класса (5.2.5), в котором либо
- тип выражения объекта является текущим экземпляром, текущее экземплярирование имеет по меньшей мере один зависимый базовый класс и поиск имени id-выражения не находит члена класса то есть текущий экземпляр или не зависящий базовый класс их; или
- тип выражения объекта зависит и не является текущим экземпляром.
Несмотря на то, что тип (*this)
зависит, он является текущим экземпляром. И поиск имени должен найти m
, чтобы быть членом текущего экземпляра.
Итак, *this
не зависит от типа и, следовательно, sizeof (*this).m
не зависит. (sizeof m
также не зависит от какого-либо нестатического члена элемента данных определения функции, который я случайно включил в свой второй, удаленный ответ).
Для sizeof std::declval<A>().m
, чтобы быть зависимым, std::declval<A>().m
должен быть зависимым от типа.
std::declval<A>().m
, похоже, зависит от типа, но я не уверен. Как указано в [temp.dep.expr]/5, который я цитировал выше, единственная возможность заключается в том, что m
в выражении является членом неизвестной специализации, которую мы должны показать.
Имя является членом неизвестной специализации, если оно
- Идентификатор, обозначающий член в выражении доступа к члену класса (5.2.5), в котором либо
- тип выражения объекта является текущим экземпляром, текущее экземплярирование имеет по меньшей мере один зависимый базовый класс и поиск имени id-выражения не находит члена класса то есть текущий экземпляр или не зависящий базовый класс их; или
- тип выражения объекта зависит и не является текущим экземпляром.
Вот факты:
-
Объектное выражение std::declval<A>()
зависит от типа.
-
Поиск std::declval<A>
выполняется только в контексте определения, поскольку он является квалифицированным идентификатором, который никогда не является зависимым именем ([temp.dep]/1).
Существует только один шаблон функции declval
, найденный с помощью поиска квалифицированного имени, но мы не можем знать, является ли возвращаемый тип этого кандидата текущим экземпляром во время определения. Здесь, в частности, add_rvalue_reference
может иметь специализации, неизвестные во время определения (сценарий, подобный этот). Поэтому, поскольку мы не знаем, является ли std::declval<A>()
текущим экземпляром, (мы предполагаем), это не так, что делает все выражение зависимым от типа.
&A::m
имеет вид &
qualified-id, который распространяется на [temp.dep.constexpr]/5:
Выражение формы &
qualified-id, где идентификатор Именем зависимого члена текущего экземпляра является Значение-зависимое.
[temp.dep.type]/5:
Имя является зависимым членом текущего экземпляра, если оно является член текущего экземпляра, который, когда он смотрит вверх, ссылается на хотя бы один член класса, который является текущим экземпляром.
Ясно, что A::m
является членом текущего экземпляра, поэтому &A::m
зависит от стоимости.
Кроме того, &A::m
зависит от типа: подвыражение A
эквивалентно A<T>
в соответствии с [temp.local], которое представляет собой простой шаблон-идентификатор с зависимыми аргументами шаблона.
Ответ 2
Ответ зависит от того, можно ли найти m
, чтобы определить, что он является членом текущего экземпляра. Идентификатор id m
преобразуется в доступ к члену класса (*this).m
, что означает, что применимы правила для поиска квалифицированного имени в доступе к члену класса.
В общем случае тип зависимого от типа выражения не может быть определен. Не совсем ясно, должно ли быть сделано исключение для (*this).
и this->
. Выражение, содержащее this
, зависит от типа, но оба (*this).
и this->
однозначно называют текущее создание экземпляра.
m
Выражение m
действительно не зависит от типа, потому что оно относится к члену текущего экземпляра.
В контексте нестатического члена m
преобразуется в выражение доступа к члену класса (*this).m
.
[class.mfct.non-статические]/3
Когда выражение id (5.1), которое не является частью синтаксиса доступа члена класса (5.2.5) и не используется для формирования указателя на член (5.3.1), используется в члене класса X
в контексте, где это можно использовать (5.1.1), если поиск по имени (3.4) разрешает имя в id-выражении нестационарному члену не-типа некоторого класса C
, и если либо id- выражение потенциально оценивается или C
является X
или базовым классом X
, выражение id преобразуется в выражение доступа к члену класса (5.2.5), используя (*this)
Преобразование происходит потому, что m
является членом класса A
, используемым в нестационарном члене класса A
.
[expr.prim.general]/3
Если объявление объявляет функцию-член или шаблон функции-члена класса X, the expression
this` является prvalue типа "указатель на cv-qualifier-seq X" между необязательным cv-qualifer-seq и концом функция-определение, член-декларатор или декларатор. Он не должен появляться перед необязательным cv-qualifier-seq и он не должен появляться в объявлении статической функции-члена (хотя его тип и значение категория определяются в рамках статической функции-члена, поскольку они находятся в нестатической функции-члене).
[expr.prim.general]/5
Выражение this
не должно появляться ни в каком другом контексте. [Пример:
class Outer {
int a[sizeof(*this)]; // error: not inside a member function
unsigned int sz = sizeof(*this); // OK: in brace-or-equal-initializer
void f() {
int b[sizeof(*this)]; // OK
struct Inner {
int c[sizeof(*this)]; // error: not inside a member function of Inner
};
}
};
-end пример]
В приведенном выше примере явно разрешено использование this
внутри выражения sizeof
внутри нестатического элемента.
[temp.dep.expr]/2
this
зависит от типа, если тип класса входящей функции-члена зависит от
Следовательно, this
зависит от типа в определении функции-члена шаблона класса.
[temp.dep.expr]/1
За исключением случаев, описанных ниже, выражение зависит от типа, если любое подвыражение зависит от типа.
Однако вышеперечисленное исключается из исключения в [temp.dep.expr]/5, цитируемого в вопросе.
this->m
Выражение this->m
также не зависит от типа, поскольку оно также является выражением доступа к члену класса, которое относится к члену текущего экземпляра.
std::declval<A>().m
Выражение std::declval<A>().m
должно быть зависимым от типа, так как тип возврата std::declval<A>()
может зависеть от типа A
.
[temp.local]/1
Подобно обычным (не шаблонным) классам, шаблоны классов имеют имя с введенным классом (раздел 9). Инъекционный сплав- имя может использоваться как имя шаблона или имя типа. Когда он используется с шаблоном-аргументом-списком, в качестве аргумента шаблона для шаблона-шаблона шаблона или в качестве конечного идентификатора в специфицированном указателе типов объявления шаблона класса друга, оно относится к самому шаблону класса. В противном случае это эквивалентно к имени шаблона, за которым следуют шаблонные параметры шаблона класса, заключенного в <>
Поэтому A
преобразуется в A<T>
.
[temp.dep.type]/8
Тип зависит, если он
-
параметр шаблона,
-
простой шаблон-идентификатор, в котором либо имя шаблона является параметром шаблона, либо любым шаблоном аргументы - это зависимый тип или выражение, зависящее от типа или зависящее от значения
Это подтверждает, что A<T>
является зависимым типом, что означает, что A
также зависит.
[temp.dep.expr]/3
Идентификатор id зависит от типа, если он содержит
- идентификатор шаблона, который зависит,
So std::declval<A>
является зависимым от типа выражением. Из этого следует, что std::declval<A>().m
зависит от типа, поскольку он содержит зависимое от типа подвыражение std::declval<A>
.
&A::m
Выражение &A::m
логически должно быть зависимым от типа, поскольку оно имеет тип int A<T>::*
, который является зависимым типом.
Выражение &A::m
преобразуется в &A<T>::m
, потому что A
- это имя введенного класса, как показано выше.
Идентификатор id A<T>::m
зависит от типа в соответствии с [temp.dep.expr]/3, потому что он содержит зависимый шаблон-id A<T>
. Поэтому выражение &A<T>::m
зависит от типа в соответствии с [temp.dep.expr]/1.
A::m
Выражение A::m
преобразуется в A<T>::m
, потому что A
- это введенное имя класса - как показано выше. Выражение A<T>::m
далее преобразуется в (*this).A<T>::m
, потому что A<T>::m
называет нестатический член A
.
Идентификатор id A<T>::m
зависит от типа в соответствии с [temp.dep.expr]/3, потому что он содержит зависимый шаблон-id A<T>
. Выражение доступа к члену класса (*this).A<T>::m
относится к члену текущего экземпляра, поэтому применяется [temp.dep.expr]/5, но не делает выражение зависимым от типа, и не противоречит [temp.dep.expr]/3.
Дефект?
Учитывая приведенную выше интерпретацию, идентификатор id, обозначающий член A
, квалифицированный с помощью A
или A<T>
, станет зависимым от типа, что кажется ненужным и непоследовательным. Учтите, что имя типа, квалифицированное с помощью A
или A<T>
, не будет зависеть от того же контекста.
Если намерение [temp.dep.expr]/5 заключается в том, что (*this).m
не зависит от типа, то из этого следует, что (*this).A<T>::m
также не зависит от типа. С другой стороны, намерение может заключаться в том, что имя нестатического элемента всегда зависит. Я задал вопрос для std-обсуждения, чтобы прояснить этот момент: https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/gEvZ7mmXEC8