【问题标题】:Why class template below compile in Visual Studio 2019 and does not with gcc 8.3?为什么下面的类模板在 Visual Studio 2019 中编译而不使用 gcc 8.3?
【发布时间】:2020-06-27 09:34:43
【问题描述】:

为什么下面的代码可以在 Visual Studio 2019 中编译,而不能在 gcc 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);
}

这是我从学生项目中提取的一个 sn-p,因为它对我来说看起来很奇怪。我用 gcc 8.3 尝试这个,因为我怀疑编译器抱怨模板参数推导失败。所以我告诉我的学生,这行不通。但他认为这确实在 VS 中编译而没有警告,我检查了一下,他是对的。

因为我离称自己为专家还很遥远,所以我无法向自己和我的学生解释是否/为什么/什么是错的。

【问题讨论】:

  • 作弊可能不是最好的选择。
  • 你说得对,我想通过,作弊''我想说,,我不明白''

标签: c++ visual-studio templates gcc


【解决方案1】:

Gcc 和 MSVC 都是正确的。 std::array的第二个模板参数的类型定义为std::size_tstd::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_tunsigned 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-qualifiers 被删除,除非模板参数是从 数组绑定——在这种情况下,任何整数类型都是允许的,甚至是 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
}

【讨论】:

  • MSVC 中的 size_t 是 64 位模式下的 64 位类型,但是上面的 sn-p 仍然可以正确编译 godbolt.org/z/_juS-o 但是是的,如果我使用 @987654337,gcc 和 Clang 会编译它@选项
  • @phuclv 可能是 MSVC 的问题;我添加了关于为什么类型不匹配导致非类型模板参数推导失败的参考。
【解决方案2】:

每当你遇到这样的模板错误时,你能做的最好的事情就是尝试不同的编译器,看看它们会给你什么错误。 CompilerExplorer 是一个非常好的网站

GCC 说:

<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 */ }

如您所见,GCC 和 Clang 都未能将 long unsigned int (std::size_t) 替换为 unsigned int

【讨论】:

  • 通常不使用“+1”类型的 cmets,但非常感谢 ComplierExplorer 的链接!
猜你喜欢
  • 2015-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-23
  • 2010-12-18
  • 2020-06-26
  • 1970-01-01
相关资源
最近更新 更多