Проблемы с пониманием зависимых от C ++ типов по сравнению с текущей реализацией - PullRequest
2 голосов
/ 02 июня 2019

Код ниже адаптирован из ответа здесь: https://stackoverflow.com/a/17579889/352552

Моя цель при задании этого вопроса - попытаться лучше понять, как C ++ обрабатывает разрешение типов вокруг зависимых типов по сравнению с тем, что считается в текущей реализации, и поэтому не нуждается в квалификаторе typename. Я получаю противоречивые результаты от разных компиляторов, поэтому я пришел сюда в поисках более канонического ответа.

Рассмотрим этот код

#include <iostream>

struct B {
  typedef int result_type;
};

template<typename T>
struct C {
};

template<>
struct C<float> {
  typedef float result_type;
}; 

template<typename T>
struct D : B, C<T> {
  std::string show() {
    //A) Default to current instantiation - ignore dependent type, even if one exists, or so I hope
    D::result_type r1;

    //B) What **exactly** does typename add, here?
    //typename D::result_type r1;

    return whichType(r1);
  }

  std::string whichType (int val){
    return "INT";
  }
  std::string whichType (float val){
    return "FLOAT";
  }    
};


int main() {  
  D<std::string> stringD;
  D<float> floatD;
  std::cout<<"String initialization "<<stringD.show()<<std::endl;
  std::cout<<"Float initialization "<<floatD.show()<<std::endl;
}

строка A) в show(), если я правильно понимаю, говорит компилятору использовать текущую реализацию, поэтому я должен получить INT INT. На GCC я делаю. Все идет нормально.

Строка B, опять же, если я правильно понимаю, должна либо сказать компилятору рассмотреть зависимые типы, что приведет к ошибке в этой строке из-за неоднозначности; или, если это означает, что только учитывает зависимые типы, я должен получить INT FLOAT. На GCC я тоже получаю INT INT. Почему?


Запуск этого на Clang.

Строка A вообще не компилируется.

ошибка: нет типа с именем 'result_type' в 'D'; Вы имели в виду просто 'result_type'? D :: result_type r1;

сброс D:: действительно дает INT INT.

Должен ли он скомпилироваться, или Clang здесь правильный?

Строка B действительно дает ошибку по неопределенности

ошибка: элемент 'result_type' найден в нескольких базовых классах разных типов, имя типа D :: result_type r1


Может ли кто-нибудь здесь с уверенностью сказать, какой компилятор (если есть!) Канонически корректен и почему?

Предполагая, что Clang верен, это может означать, что

MyType::F

недопустимо для ссылки на тип из текущего экземпляра, если он существует для базового типа; это допустимо, только если тип определен в этом классе. Т.е. добавление

typedef double dd;

до D

, а затем

D::dd d = 1.1;
std::cout<<d;

в show будет работать нормально, что действительно так.

* * Тысяча шестьдесят-два Кроме того,

typename D::sometype

, кажется, означает рассмотрение зависимых типов, но не исключительно, и поэтому ожидайте ошибок, если такой тип будет определен в нескольких местах, либо в текущем экземпляре, либо в зависимости от параметра шаблона.

Но опять же, все это предполагает, что поведение Clang является правильным в соответствии со спецификацией, с которой я не могу говорить.


Ссылка на GCC Repl, который я использовал: https://wandbox.org/

Ссылка на реплан Clang, который я использовал: https://repl.it/languages/cpp11

1 Ответ

3 голосов
/ 02 июня 2019

Более того,

typename D::sometype

, кажется, означает рассмотреть зависимые типы

Откуда у вас эта идея? typename означает только то, что ниже следует не элемент данных, а имя типа, так что можно выполнить синтаксический анализ шаблона. Знаете ли вы, как примитивные компиляторы C ++ анализировали шаблонные функции и классы в старые времена? Они не делали значимого разбора, они просто съели все символы, выполняя только балансировку { / }. Да, в какой-то момент вы можете включить почти любой мусор в определения шаблонов, если они никогда не создавались! Это было упрощенно и грязно, но не настолько безрассудно, если подумать, поскольку альтернатива (правильный синтаксический анализ) в то время была неосуществима.

Чтобы даже осмысленно анализировать (даже не разрешая много имен) внутри шаблона, необходимо сделать некоторые вещи явными: категорию (переменная или функция, имя типа, имя шаблона) символов, которые не могут быть разрешены до реализации такие простые вещи, как X * Y;, X * (Y); и X(Y);, неоднозначны и не могут быть проанализированы (объявление или выражение). Таким образом, typename используется, чтобы указать, что символ, который не может быть найден во время определения шаблона, обозначает тип , поэтому, если X равен typename T::U, тогда все три предыдущих синтагмы являются объявлением; без typename и если T::U является зависимым, они будут проанализированы как выражения-выражения, и при создании экземпляров не будет никакого второго анализа, поэтому, если U действительно является типом, это будет ошибкой.

//A) Default to current instantiation - ignore dependent type, even if one exists, or so I hope
D::result_type r1;

Согласно https://en.cppreference.com/w/cpp/language/dependent_name поиск из «текущего экземпляра» рассматривает только независимые базовые классы во время определения, а затем:

Если поиск члена текущего экземпляра дает другое результат между точкой создания и точкой определения, поиск неоднозначен.

Так что, надеюсь, то, на что вы «надеялись», не должно произойти!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...