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


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

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

Другие вопросы по тегам