Является ли имя нестатического члена зависимым при использовании в нестатической функции-члене?

Оба 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

Имя является членом неизвестной специализации, если оно

  • Идентификатор, обозначающий член в выражении доступа к члену класса (5.2.5), в котором либо

    • тип выражения объекта является текущим экземпляром, текущий экземпляр имеет как минимум один зависимый базовый класс и поиск имени id-выражения не находит члена текущий экземпляр или его независимый базовый класс; или

    • тип выражения объекта зависит и не является текущим экземпляром.

Итак, 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