Когда я писал код для своего проекта, я обнаружил очень странную ситуацию, в которой мой C ++ 11 код не мог быть кросс-скомпилирован между GCC 7.3 и MSVC2015 .Дело в основном в следующем:
// .H file
template <typename X>
struct Outer {
X x = {};
template <typename Y>
struct Inner {
Y y = {};
Inner& operator++();
};
};
// .INL file
template <typename X>
template <typename Y>
inline Outer<X>::Inner<Y>&
Outer<X>::Inner<Y>::operator++()
{
++y;
return *this;
}
// .CPP file
#include <iostream>
int main()
{
Outer<int>::Inner<int> a;
++a;
std::cout << a.y << std::endl;
return 0;
}
GCC не будет жаловаться на приведенный выше код.Но MSVC будет, и ошибка будет выглядеть следующим образом:
warning C4346: 'Inner': dependent name is not a type note: prefix with
'typename' to indicate a type error C2061: syntax error: identifier
'Inner' error C2143: syntax error: missing ';' before '{' error C2447:
'{': missing function header (old-style formal list?)
Как предполагает сам компилятор MSVC, написание ключевого слова typename
для возвращаемого типа решит проблему:
template <typename X>
template <typename Y>
inline typename Outer<X>::Inner<Y>&
Outer<X>::Inner<Y>::operator++()
{
++y;
return *this;
}
Но теперь, что удивительно, GCC больше не будет компилировать, и его ошибка:
error: non-template 'Inner' used as template
inline typename Outer<X>::Inner<Y>&
^~~~~ note: use 'Outer<X>::template Inner' to indicate that it is a template error: expected
unqualified-id at end of input
Опять же, как предложил сам компилятор, я попытался добавить ключевое слово template
к возвращаемому типу,и это исправит проблему для GCC:
template <typename X>
template <typename Y>
inline typename Outer<X>::template Inner<Y>&
Outer<X>::Inner<Y>::operator++()
{
++y;
return *this;
}
Но теперь это исправление снова сломает сборку MSVC, и ошибка компилятора:
error C2244: 'Outer<X>::Inner<Y>::operator ++': unable to match
function definition to an existing declaration note: see declaration
of 'Outer<X>::Inner<Y>::operator ++' note: definition note:
'Outer<X>::Inner<Y> &Outer<X>::Inner<Y>::operator ++(void)' note:
existing declarations note: 'Outer<X>::Inner<Y>
&Outer<X>::Inner<Y>::operator ++(void)'
На этот разЯ просто остановился, так как сообщение об ошибке компилятора не является полезным, поскольку сообщаемое несоответствие между определением и объявлением не существует: сигнатуры, данные самим компилятором, полностью совпадают.
На данный момент, не зная ничего лучшегоВ качестве решения я решил просто определить функцию внутри области действия класса Inner в файле .H, и это будет работать как для GCC, так и для MSVC.Но некоторые вопросы все еще остаются: кто прав?GCC или MSVC?Или это стандарт C ++, который в этом случае слишком неоднозначен?