Почему шаблон класса ниже компилируется в Visual Studio 2019, а не с g cc 8.3? - PullRequest
6 голосов
/ 16 марта 2020

Почему код ниже компилируется в Visual Studio 2019, а не с g cc 8.3?

#include<array>
template<typename T> class myClass {
public:
template<unsigned int N> myClass(const std::array<T, N>& elems) { /* do something */ }
};

int main() {
    std::array<int, 10> A;
    myClass<int> Tabular(A);
}

Это фрагмент, который я извлек из проекта моего студента, потому что он выглядит странно для меня. Я пытаюсь сделать это с g cc 8.3 и, как я подозреваю, компилятор жалуется, что вывод аргумента шаблона не удался. Поэтому я сказал своему ученику, что это не работает. Но он утверждал, что это сделать компиляцию в VS без предупреждения, я проверил, и он был прав.

Поскольку я очень далек от того, чтобы называть себя экспертом, я не могу объяснить себе и моему ученику, что / почему / что не так.

Ответы [ 2 ]

5 голосов
/ 16 марта 2020

И G cc, и MSV C верны. Тип 2-го параметра шаблона std::array определяется как std::size_t, а способ определения std::size_t зависит от реализации.

typedef /*implementation-defined*/ size_t;

std::size_t - это целочисленный тип без знака результата оператора sizeof as well as the sizeof... operator and the alignof operator (since C++11).

Ширина бита std::size_t не меньше 16. (начиная с C ++ 11)

Тогда, когда std::size_t определено так же, как unsigned int, код компилируется нормально, в противном случае произойдет сбой; несоответствие типов приводит к сбою вывода аргументов не типового шаблона.

Измените unsigned int на std::size_t, тогда код гарантированно будет хорошо скомпилирован любыми приличными компиляторами. Например,

template<typename T> class myClass {
public:
template<std::size_t N> myClass(const std::array<T, N>& elems) { /* do something */ }
};

И о том, почему несоответствие типов приводит к сбою вывода аргумента шаблона нетипичного типа.

(выделение мое)

Если шаблон нетипичного типа Параметр используется в списке параметров, и выводится соответствующий аргумент шаблона, тип аргумента выведенного шаблона (как указано в прилагаемом списке параметров шаблона, то есть ссылки сохраняются) должен соответствовать типу нетипичного параметр шаблона в точности , за исключением того, что cv-квалификаторы отбрасываются и кроме случаев, когда аргумент шаблона выводится из границы массива - в этом случае допускается любой целочисленный тип, даже bool, хотя он всегда становится истинным:

template<int i> class A { };
template<short s> void f(A<s>); // the type of the non-type template param is short

void k1()
{
    A<1> a;  // the type of the non-type template param of a is int
    f(a);    // P = A<(short)s>, A = A<(int)1>
             // error: deduced non-type template argument does not have the same
             // type as its corresponding template argument
    f<1>(a); // OK: the template argument is not deduced, 
             // this calls f<(short)1>(A<(short)1>)
}

template<int&> struct X;
template<int& R> void k2(X<R>&);
int n;
void g(X<n> &x) {
    k2(x); // P = X<R>, A = X<n>
           // parameter type is int&
           // argument type is int& in struct X's template declaration
           // OK (with CWG 2091): deduces R to refer to n
}
2 голосов
/ 16 марта 2020

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

G CC говорит:

<source>:4:26: note: candidate: 'template<unsigned int N> myClass<T>::myClass(const std::array<T, N>&) [with unsigned int N = N; T = int]'

    4 | template<unsigned int N> myClass(const std::array<T, N>& elems) { /* do something */ }

      |                          ^~~~~~~

<source>:4:26: note:   template argument deduction/substitution failed:

<source>:9:27: note:   mismatched types 'unsigned int' and 'long unsigned int'

    9 |     myClass<int> Tabular(A);

      |  

Clang говорит:

<source>:4:26: note: candidate template ignored: substitution failure: deduced non-type template argument does not have the same type as the corresponding template parameter ('unsigned long' vs 'unsigned int')

template<unsigned int N> myClass(const std::array<T, N>& elems) { /* do something */ }

Как вы можете видите, и G CC, и Clang не смогли заменить long unsigned int (std::size_t) с unsigned int.

...