Все версии GCC борются с шаблоном, который имеет тип по умолчанию в определении
Я потратил бесчисленные часы, чтобы точно определить проблему с gcc. Я хотел проверить нашу кодовую базу с другим компилятором, чтобы найти больше предупреждений, которые Clang мог пропустить. Я был шокирован, что практически половина проекта перестала компилироваться из-за сбоя вывода аргументов шаблона. Здесь я попытался превратить мой случай в простейший фрагмент кода.
#include <type_traits>
struct Foo
{ };
// This is a template function declaration, where second template argument declared without a default
template <typename T, typename>
void foo(const Foo & foo, T t);
// This is a template function definition; second template argument now has a default declared
template <typename T, typename = typename std::enable_if<1>::type>
void foo(const Foo & foo, T t)
{
}
int main(int argc, char ** argv)
{
foo(Foo{}, 1);
return 0;
}
Игнорировать 1
в std::enable_if<1>
, Очевидно, это постоянное значение, чтобы не усложнять ситуацию, когда это не имеет значения.
Этот фрагмент кода компилируется [1] с помощью clang (от 3.4 до 4.0), icc (16, 17), Visual C++ (19.00.23506). По сути, я не смог найти другого компилятора C++11, который, кроме gcc (с 4.8 по 7.1), не компилирует этот кусок кода.
Вопрос в том, кто прав, а кто здесь не прав? Gcc ведет себя по стандарту?
Очевидно, что это не критическая проблема. Я могу легко двигаться std::enable_if
к декларации. Единственной жертвой будет эстетика. Но приятно иметь возможность скрывать уродливые 100 символов std::enable_if
кусок кода, который не имеет непосредственного отношения к пользователю библиотечной функции в реализации.
Живой пример на godbolt.org.
1 ответ
Что говорится в стандарте ( [1] стр. 350):
Набор аргументов шаблона по умолчанию, доступных для использования с объявлением или определением шаблона, получается путем объединения аргументов по умолчанию из определения (если в области) и всех объявлений в области таким же образом, как аргументы функции по умолчанию (8.3.6). [ Пример:
template<class T1, class T2 = int> class A; template<class T1 = int, class T2> class A; is equivalent to template<class T1 = int, class T2 = int> class A;
- конец примера]
Так что GCC здесь не так. Он игнорирует аргументы шаблона по умолчанию в объявлениях.
Не все объявления, только объявления шаблонов функций. Объявления шаблона класса в порядке:
#include <type_traits>
template <typename T, typename>
struct Foo;
template <typename T, typename = typename std::enable_if<1>::type>
struct Foo
{
T t;
};
int main()
{
Foo<int> foo;
return 0;
}
Живой пример на godbolt.org
Вероятно, это связано с тем, как выводятся аргументы не по умолчанию. В шаблоне функции они вычитаются из аргументов функции. В шаблоне класса мы должны указать их явно.