Все версии 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;
}
Игнорировать a 1
в std::enable_if<1>
. Очевидно, это постоянное значение, чтобы не усложнять ситуацию, когда это не имеет значения.
Этот фрагмент кода компилирует [1] с clang (с 3.4 по 4.0), icc (16, 17), Visual С++ (19.00.23506). В принципе, я не смог найти какой-либо другой компилятор С++ 11, который, кроме gcc (4.8 - 7.1), не компилирует этот фрагмент кода.
Вопрос в том, кто прав и кто здесь? Поддерживает ли gcc поведение в соответствии со стандартом?
Очевидно, что это не критическая проблема. Я могу легко переместить std::enable_if
в объявление. Единственной жертвой была бы эстетика. Но хорошо иметь возможность скрыть уродливые 100 символов длиной std::enable_if
фрагмента кода, который не имеет непосредственного значения для пользователя библиотечной функции, в реализации.
Пример Live на godbolt.org.
Ответы
Ответ 1
Что говорит стандарт ([1], стр. 350):
Набор шаблонов-шаблонов по умолчанию, доступных для использования с объявление шаблона или определение получают путем слияния по умолчанию аргументы из определения (если в области) и все объявления в scope аналогичным образом аргументы функции по умолчанию (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
Вероятно, это связано с тем, как выводятся аргументы, отличные от дефолта. В шаблоне функции они вычитаются из аргументов функции. В шаблоне класса мы должны явно указывать их.
В любом случае, я создал отчет об ошибке .