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

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

Я хотел бы спросить, должен ли компилироваться следующий образец кода:

#include <iostream>
#include <vector>
#include <typeinfo>

using namespace std;

template <template <class...> class C>
struct convert_container
{
    using type = C<double>; 

    // Visual Studio requires this to be:
    // using type = C<double, std::allocator<doble>>
};

int main()
{
    std::cout << typeid(convert_container<std::vector>::type).name();
}

Код компилируется с GCC 4.8.1 и Clang 3.4, но не с Visual Studio 2013. Ошибка, которую я получаю:

error C2976: 'std::vector' : too few template arguments
    c:\program files (x86)\microsoft visual studio 12.0\vc\include\vector(650) : see declaration of 'std::vector'
    c:\users\michał\documents\visual studio 2013\projects\transform\transform.cpp(14) : see reference to class template instantiation 'convert_container<std::vector>' being compiled

Что говорит об этом стандарт? Мне нужно явно указывать все параметры (включая дефолтные) при использовании параметра шаблона шаблона C или это просто ошибка в VС++?

Контекст: проблема, вызванная конструктором, отвечает на мой предыдущий вопрос: qaru.site/info/144397/...

При поиске в архивах я нашел этот вопрос: Значения по умолчанию в шаблонах с аргументами шаблона (С++) В основном по той же проблеме автор вопроса утверждает, что параметры по умолчанию для параметра шаблона шаблона "должны быть" явно указаны. Однако, искатель принял решение, которое не совсем применимо в моем случае. Вопрос не в том, что такое стандартно-совместимое поведение, поэтому я считаю, что это не дубликат.

4b9b3361

Ответ 1

Рассмотрим аналогичный

template <typename = void, int = 0> struct A { };
template <template <typename ...> class T> struct B : T<> { };
template class B<A>;

Это явно покрывается стандартом (14.3.3p3, если вам интересно, я не буду его процитировать, поскольку GCC и clang уже выполняют правило), где использование A в качестве аргумента шаблона для B запрещается из-за параметра шаблона непигового типа. Это правило не имеет смысла, если создание шаблона шаблона может использовать аргументы шаблона шаблона шаблона шаблона шаблона, поэтому поведение MSVC и Intel более последовательное, чем поведение GCC и clang.

Конечно, рассуждение "если бы это было справедливо, стандарт имел бы несогласованности" на самом деле не означает, что он недействителен, только то, что он не должен быть действительным. Чтобы проверить, что говорит стандарт:

14.1 Параметры шаблона [temp.param]

10 Набор шаблонов-шаблонов по умолчанию, доступных для использования с объявлением или определением шаблона, получается путем слияния аргументов по умолчанию из определения (если в области видимости) и всех объявлений в области видимости аналогично аргументам функции по умолчанию (8.3 0,6).

8.3.6 Аргументы по умолчанию [dcl.fct.default]

4 Объявления в разных областях имеют совершенно разные наборы аргументов по умолчанию. То есть объявления во внутренних областях не принимают аргументы по умолчанию из объявлений во внешних областях и наоборот.

Хотя это специально не предназначено для решения этой проблемы с использованием аргументов шаблона по умолчанию, я думаю, что это действительно так. Nikos Athanasiou уже включил часть стандарта, в которой говорится, что любые аргументы шаблона по умолчанию C можно использовать:

14.1 Параметры шаблона [temp.param]

14 Параметру шаблона шаблона-шаблона допускается иметь шаблон-аргумент по умолчанию. Когда такие аргументы по умолчанию указаны, они применяются к шаблону-шаблону шаблона в области шаблона-шаблона шаблона.

Поскольку C используются аргументы шаблона по умолчанию, std::vector нет, а MSVC и Intel, похоже, здесь верны.

И придумать пример, который ясно показывает, что GCC и clang не могут считаться соответствующими здесь:

template <typename = char, typename = short>
struct A { };

template <template <typename = void, typename ...> class T>
struct B {
  using type = T<>;
};

Оба GCC и clang обрабатывают B<A>::type как A<void, short>, беря один аргумент шаблона по умолчанию из T, а другой из A, даже если стандарт запрещает слияние аргументов по умолчанию (и, следовательно, аргументы шаблона по умолчанию) в деклараций в разных областях.


Обходной путь для вас, чтобы избежать необходимости вводить аргумент распределителя, можно было бы использовать псевдоним шаблона:

template <template <class...> class C>
struct convert_container
{
  using type = C<double>; 
};

template <typename T>
using vector_default_alloc = std::vector<T>;

int main()
{
  std::cout << typeid(convert_container<vector_default_alloc>::type).name();
}

Я не могу тестировать MSVC прямо сейчас, но Intel принимает его, и я не вижу причин, почему этот вариант был бы недействительным.

Ответ 2

A (казалось бы, связанная) цитата из стандарта 14.1 Template parameters

14. Параметру шаблона шаблона-шаблона разрешено иметь шаблон-аргумент по умолчанию. Когда такие аргументы по умолчанию указаны, они применяются к шаблону-шаблону шаблона в области шаблона-шаблона шаблона.

[Пример:

template <class T = float> struct B {};

template <template <class TT = float> class T> struct A {

inline void f();

inline void g();
};

template <template <class TT> class T> void A<T>::f() { // (*)
T<> t; // error - TT has no default template argument
}

template <template <class TT = char> class T> void A<T>::g() {
T<> t; // OK - T<char>
}

- конец примера]

Это единственный стих, устанавливающий ограничения использования параметров шаблона по умолчанию параметров шаблона шаблона (стихи 9,11,12 представляют ограничения для определения/спецификации)

Как подчеркивается в комментариях, случай OP не включает параметр по умолчанию в convert_container (поэтому приведенное выше не применяется явно). ИМХО существует два способа интерпретации ситуации:

  • using type = C<double> - это псевдоним типа для шаблона класса; этот класс "теряет" право использовать параметры шаблона по умолчанию, поскольку он передается в качестве параметра шаблона шаблона, и все аргументы по умолчанию (этого параметра TT) лежат вне области "typedefing". Тогда VS правильно.

  • отслеживание процесса создания: скажем, что правильный компилятор создает экземпляр структуры как таковой (это просто подстановка типа - не подразумевается фактическое представление фактического процесса создания экземпляра)

    struct convert_container
    {
        using type = vector<double>; 
    };
    

тогда случай OP кажется довольно законным (и gcc/clang верны)


FWIW

Этот компилирует в VS2013

template <template <class...> class C>
using tt = C<double>;

int main()
{
    std::cout << typeid(tt<std::vector>).name();
}

Таким образом, аргументы параметров шаблона по умолчанию, являющиеся не законными для перехода к параметрам шаблона шаблона, кажутся все более шаткими.