Подтвердить что ты не робот

Все версии 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.

4b9b3361

Ответ 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


Вероятно, это связано с тем, как выводятся аргументы, отличные от дефолта. В шаблоне функции они вычитаются из аргументов функции. В шаблоне класса мы должны явно указывать их.

В любом случае, я создал отчет об ошибке .