Использование typealias вместо typedef, определенного в классе в определении

#c #gcc #compiler-bug

#c #gcc #ошибка компилятора

Вопрос:

Почему это не удается скомпилировать? Я пытаюсь уменьшить количество повторений, поскольку в реальном коде имя класса огромно, а на самом type деле это очень длинное имя

https://godbolt.org/z/8YarWs

 #include <vector>

template<typename T>
struct S {
    using type = std::vector<T>;

    type f() const;
};

template<typename T>
using type_t = typename S<T>::type;

template <typename T>
type_t<T> S<T>::f() const { }
 

Приведенный выше код завершается с ошибкой, как показано ниже

 <source>:17:14: error: no declaration matches 'type_t<T> S<T>::f() const'
   17 | type_t < T > S < T >::f() const
      |              ^~~~~~~
<source>:8:10: note: candidate is: 'S<T>::type S<T>::f() const'
    8 |     type f() const
      |          ^
<source>:4:8: note: 'struct S<T>' defined here
    4 | struct S
      |        ^
 

Комментарии:

1.Обратите внимание, что любое создание экземпляра вашего шаблона будет нарушать [basic.def.odr]/1 , поскольку у вас есть несколько определений f() для каждого набора аргументов tempate (/каждой специализации S ). Вы имели в виду объявление f() только в определении класса? В любом случае это отвлекающий маневр, и похоже, что вы обнаружили ошибку GCC (clang компилирует исправленный пример).

2. Я также продолжаю сталкиваться с этой ошибкой, я подозреваю, что это как-то связано с тем, что gcc обрабатывает псевдонимы типов как промежуточное представление. Clang компилирует это нормально: godbolt.org/z/aYYM3c Примечание: вы также можете просто справиться с этим в gcc, объявив f следующим образом: type_t<T> f() const;

3. В качестве обходного пути вы можете использовать auto S<T>::f () const -> type (завершающий возвращаемый тип).

4. Спасибо за исправление в объявлении класса

5. Мне пришлось написать признак типа, совместимый с C 11, чтобы проверить, являются ли два класса шаблонов экземплярами одного и того же шаблона (например: S<T> и S<U> ). Однако казалось, что всякий раз, когда при использовании с псевдонимом типа: template <typename T> using V = S<T> все мои утверждения будут терпеть неудачу, если я не сравню их результаты (т. Е. Два шаблона не были равны, но результат типа того, что они дали в a decltype , был). Странно, что он существует уже более 5 основных ревизий, но, честно говоря, это немного больше, чем раздражение.

Ответ №1:

Это ошибка GCC; ближайший (1) подтвержденный открытый отчет об ошибке, похоже,:

  • Ошибка 69348: объявления псевдонимов не могут использоваться внутри квалификаторов деклараторов

что подчеркивает, что GCC отклоняет следующую хорошо сформированную программу:

 template <class T>
struct X {
    int foo();
};

template <class T>
using foo2 = X<T>;

template <class T>
int foo2<T>::foo()
{
}
 

с сообщением об ошибке

 error: invalid use of incomplete type 'struct X<T>'
 

(1) Сканирование GCC: s bugzilla, похоже, содержит многочисленные «отклоняющие действительные» (открытые / неподтвержденные) сообщения об ошибках, относящиеся к шаблонам псевдонимов, где в большинстве отчетов сообщается, что clang принимает (возможно, хорошо сформированные) примеры программ.

Ответ №2:

Я не помню точную причину, но я знаю, что это как-то связано с последовательностью, в которой компилятор разрешает имена типов и символы …
Вы могли бы заставить его работать, немного изменив структуру объявления.

Поскольку я не знаю, насколько это возможно для вас, я включил несколько вариантов.

https://godbolt.org/z/bGPGhW

 #include <vector>
#include <iostream>

template <typename T>
struct S
{
    using type = std::vector<T>;

    auto f() const -> decltype(type());
    auto g() const -> decltype(type()); 
    decltype(type()) h() const;
};

template <typename T>
using type_t = typename S<T>::type;

template <typename T>
auto S<T>::f() const -> decltype(type())
{
    std::cout << "foo" << std::endl;
    type_t<T> t;
    return t;
}

template <typename T>
using other_type_t = decltype(typename S<T>::type());

template <typename T>
auto S<T>::g() const -> other_type_t<T>
{
    std::cout << "bar" << std::endl;
    other_type_t<T> t;
    return t;
}

template <typename T>
using last_type_t = decltype(typename S<T>::type());

template <typename T>
last_type_t<T> S<T>::h() const
{
    std::cout << "foobar" << std::endl;
    last_type_t<T> t;
    return t;
}

int main(){
    S<int> s;
    auto t = s.f();
    auto r = s.g();
    auto l = s.h();
    return 0;
}
 

Вывод:

 Program returned: 0
Program stdout

foo
bar
foobar