Visual Studio 2013编译成功但有明显错误。

3

我是使用Visual Studio Professional 2013的。我遇到了一个相当奇怪的问题。通常人们会发布有关错误的文章,而我在这里发布的是没有错误的情况。

我编写了一个自定义矩阵类(为了完成作业)。 我已覆盖了分配运算符,如下所示:

template<typename T>
Matrix<T>& Matrix<T>::operator=(const Matrix<T> &other) {
    if (this != &other) {
        if (this->mRows != other.mRows || this->nColumns != other.nColumns) {
            deleteMatrixArray();
            this->mRows = other.mRows;
            this->nColumns = other.nColumns;
            newMatrixArray();
        } // else reuse the existing array
        // copy contents
        for (unsigned int i = 0; i < this->mRows; i++) {
            for (unsigned int j = 0; j < this->nColumns; j++) {
                this->matrix[i][j] = other.matrix[i][j];
            }
        }
    }
    return *this;
}

我最近修改了newMatrixArray()方法,使其接受一个布尔参数:

template<typename T>
void Matrix<T>::newMatrixArray(bool init) {
    this->matrix = new T*[this->mRows];
    for (unsigned int i = 0; i < this->mRows; i++) {
        if (init) {
            this->matrix[i] = new T[this->nColumns]();
        } else {
            this->matrix[i] = new T[this->nColumns];
        }
    }
}

然而,即使是在这种情况下,Visual Studio 仍然可以成功编译... 除非...
#include "Matrix.h"

int main() {

    Matrix<int> matrix;

    Matrix<int> otherMatrix;
    otherMatrix = matrix;

    return 0;
}

我编写了一些使用重载赋值运算符的代码。这让我很担心,因为我不知道还有什么其他问题,而 Visual Studio 没有提醒我!

出现了什么情况?

更多信息:
正如您所见,我正在使用模板。所有 Matrix 代码都在 Matrix.h 文件中 - 声明后跟定义。当使用模板时,这是必需的。Matrix 类是我目前项目中唯一的类,除了 main.cpp 文件以外。我已经检查并确保声明和定义匹配。

来源:Praetorian
编辑:(解决方案)
您可以使用:

template class NameOfClass<NameOfType>;

为了针对特定类型编译模板类。

你还可以使用:

template ReturnType NameOfFunction(Args ... );

编译具有模板参数的类外方法。

这些应放置在全局范围内。


请提供一个完整但最小的示例,以便读者可以尝试。 - Cheers and hth. - Alf
3
这并不是错误的行为,类模板的成员函数只有在使用时才会实例化。如果您想强制实例化,则可以在 main.cpp 的全局范围内显式实例化 Matrix<int>,添加一行 template class Matrix<int>;。然后,即使没有 otherMatrix = matrix; 赋值,您的代码也无法编译。 - Praetorian
2
看起来与 这个问题 非常相似,基本上只有在尝试使用它时才会失败。 - Retired Ninja
@Praetorian 谢谢!这揭示了类方法实现中的几个错误。我还重载了输入/输出运算符,但它们是“外部”的类,仍未得到检查。 :( 我想我必须手动使用它们才能使它们编译。 - Bradley Odell
1
@BradleyOdell 你也可以使用函数模板做同样的事情,所以像 template std::ostream& operator<<<int>(std::ostream&, Matrix<int> const&); 这样的东西。 - Praetorian
@Praetorian 你是最棒的 :) - Bradley Odell
3个回答

1
您说:

这让我担心,因为我不知道还有什么可能出问题,而且Visual Studio没有告诉我!

这是怎么回事?

编译器的行为并没有问题。如果一个类模板的成员函数没有被使用,那么该函数就不会被实例化。无论函数是否被实例化,某些错误都会报告,如括号不匹配,但其他错误(如您提到的错误)只有在函数被实例化时才会报告。请放心。

@n.m.,我快速浏览了与模板实例化相关的标准部分,但没有找到任何可以证明MSVC行为不符合标准的确定性证据。我会更仔细地查看。 - R Sahu
抱歉,那是我的错误。我所考虑的错误在这里并不相关。newMatrixArray是一个依赖名称,因此不应该被检查。问题在于MSVC甚至对于非依赖名称也不进行检查。 - n. m.

0

标准基本要求在声明时执行大多数检查。只有无法在此时执行的检查才会推迟到实例化时进行。后者涉及到的是涉及到依赖名称的检查。详细解释请参见这里

由于newMatrixArray是依赖性的,因此直到实例化时才会进行检查。如果成员函数从未被使用,则不会被实例化。

这个规则的原因是依赖名称的含义在模板参数未知时并不完全清楚。

(至少某些版本的)MSVC的问题在于,即使是非依赖性的名称也要等到实例化时才进行检查。您可以尝试编译此程序以检查您的编译器是否受到影响。如果编译成功,则存在错误。


0

如果模板中的某些内容没有被使用,它也不会被(完全)解析,并且不会出现错误。
这基本上必须是这样的,这不是 VS 的 bug,而是符合规范的行为。
考虑以下情况:

template<typename T>
class X
{
public:
    void work_always(const T&) {}
    void requires_copyable(T) {}
};

如果您在不可复制的类型上实例化它,只要您仅使用work_always,它仍然可以正常工作。

标准库甚至使用了这个方法,例如需要T是默认可构造的向量调整大小,但是向量仍然可以用于不是这样的类型,只要您不调用任何需要它的方法。

当然,这使得模板测试有点困难,您必须确保使用所有内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接