#c #gcc #compiler-bug
#c #gcc #ошибка компилятора
Вопрос:
Почему это не удается скомпилировать? Я пытаюсь уменьшить количество повторений, поскольку в реальном коде имя класса огромно, а на самом type
деле это очень длинное имя
#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>
все мои утверждения будут терпеть неудачу, если я не сравню их результаты (т. Е. Два шаблона не были равны, но результат типа того, что они дали в adecltype
, был). Странно, что он существует уже более 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:
Я не помню точную причину, но я знаю, что это как-то связано с последовательностью, в которой компилятор разрешает имена типов и символы …
Вы могли бы заставить его работать, немного изменив структуру объявления.
Поскольку я не знаю, насколько это возможно для вас, я включил несколько вариантов.
#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