Недопустимые аргументы шаблона шаблона

Приведенный ниже код не может скомпилироваться с gcc 7.1.0, который жалуется на то, что неверно задает число аргументов шаблона во второй строке main. Эта версия GCC должна реализовывать аргумент шаблона для шаблонов классов.

Я думаю, что компилятор должен иметь возможность выводить аргумент шаблона класса T2 для Bar, что означает, что мне не нужно явно указывать оба аргумента (Bar<int, int>), учитывая параграф 17.8.1.3 С++ 17 draft, который гласит: "Исходные аргументы шаблона, которые могут быть выведены (17.8.2) или получены из шаблонов-шаблонов по умолчанию, могут быть опущены из списка явных шаблон-аргументы".

Неужели я ошибаюсь? Неправильно ли компилятор? Это надзор или преднамеренный дизайн?

template <typename T>
struct Foo {
    Foo(T t) {}
};

template <typename T1, typename T2>
struct Bar {
    Bar(T2 t) {}
};

template <typename T1, typename T2>
void bar(T2 t) {}

int main(int argc, char **argv) {

    Foo(42); // Works

    Bar<int>(42); // Fails to compile with "wrong number of
                  // template arguments (1, should be 2)"

    bar<int>(42); // Works
}

Ответ 1

Это ожидаемое поведение; в отличие от вывод аргумента шаблона (для шаблонов функций), вывод шаблона шаблона класса (поскольку С++ 17) работает только тогда, когда аргументы шаблона не предоставляются.

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

std::tuple t(1, 2, 3);              // OK: deduction
std::tuple<int,int,int> t(1, 2, 3); // OK: all arguments are provided
std::tuple<int> t(1, 2, 3);         // Error: partial deduction

Это означает, что для вашего примера вы не можете использовать вывод аргумента шаблона шаблона и должны указывать все аргументы шаблона. Если вы хотите, чтобы аргумент аргумента шаблона шаблона вступил в силу, вам необходимо указать его, но параметр шаблона T1 не может быть выведен.

С другой стороны, будет работать следующий код.

template <typename T1, typename T2>
struct Bar {
    Bar(T1, T2) {}   // make it possible to deduce T1
};

int main(int argc, char **argv) {
    Bar bar(42, 42); // take advantage of class template argument deduction
}