如何解决没有外部库的链接错误“LNK2019:未解析的外部符号”
我在处理一个稍大的项目时似乎遇到了链接器问题。 (我正在使用 Visual Studios 2019。我正在尝试重新创建 Lubos Briedas “plasma Simulation by Example”中的代码,书中有一些错误,尽管其中大部分内容都很好,是对 C++ 模拟的很好的介绍。)
目前我收到以下错误:
Output.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Field_<double> &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Field_@N@@@Z) referenced in function "void __cdecl Output::fields(class World &,class std::vector<class Species,class std::allocator<class Species> > &)" (?fields@Output@@YAXAAVWorld@@AAV?$vector@VSpecies@@V?$allocator@VSpecies@@@std@@@std@@@Z)
Output.obj : error LNK2019: unresolved external symbol "class std::basic_ostream<char,class Field_<struct vec3<double> > &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Field_@U?$vec3@N@@@@@Z) referenced in function "void __cdecl Output::fields(class World &,class std::allocator<class Species> > &)" (?fields@Output@@YAXAAVWorld@@AAV?$vector@VSpecies@@V?$allocator@VSpecies@@@std@@@std@@@Z)
Species.obj : error LNK2019: unresolved external symbol "public: void __thiscall Field_<double>::scatter(struct vec3<double>,double)" (?scatter@?$Field_@N@@QAEXU?$vec3@N@@N@Z) referenced in function "public: void __thiscall Species::computeNumberDensity(void)" (?computeNumberDensity@Species@@QAEXXZ)
我已经多次检查了消息中提到的函数的拼写,并检查了是否没有额外的定义。我还查看了 operator<<
重载是否可以在类之外完成(如代码中),似乎没问题。将 const
添加到错误消息中的函数并不能解决它们,所以我认为它与 l/rvaulues 无关。我发现此错误的大多数其他解决方案包括通过属性页向链接器添加一些内容,但由于我不包含任何特殊的外部库,我不知道我必须在那里添加什么以及是否需要添加某些内容全部添加。
是否还有其他可能导致此错误的可能性?以及如何检测和解决需要添加或更改的内容? 我已经被困了很长一段时间,希望你们中的一个人能够帮助我。
(如果需要,我可以提供完整的代码,但我暂时不提供,因为我没有最小的工作示例,而且可能没有太多。)
// Output.h
#pragma once
#include <sstream>
#include <fstream>
#include <ostream>
#include <iostream>
#include "Fields_.h"
#include "World.h"
#include "Species.h"
namespace Output { void fields(World& world,std::vector<Species> &species); }
void Output::fields(World& world,std::vector<Species> &species);
// Output.cpp
#include "Output.h"
// write data to a file stream
template<typename T>
std::ostream& operator<<(std::ostream& out,Field_<T>& f) {
for (int k = 0; k < f.nk; k++,out << "\n") // new line after each "k"
for (int j = 0; j < f.nj; j++)
for (int i = 0; i < f.ni; i++)
out << f.data[i][j][k] << " ";
return out;
}
// saves output in VTK format
void Output::fields(World& world,std::vector<Species>& species) {
std::stringstream name; // build file name
name << "fields.vti"; // here we just set it to a given string
// open output file
std::ofstream out(name.str());
if(!out.is_open()) { std::cerr << "Coulld not open " << name.str() << std::endl; return; }
// ImageData is a VTK format for structured Cartesian meshes
out << "<VTKFile type=\"ImageData\">\n";
double3 x0 = world.getX0();
double3 dh = world.getDh();
out << "<ImageData Origin=\"" << x0[0] << " " << x0[1] << " " << x0[2] << "\" ";
out << "Spacing=\"" << dh[0] << " " << dh[1] << " " << dh[2] << "\" ";
out << "WholeExtent=\"0 " << world.ni - 1 << " 0 " << world.nj - 1 << " 0 " << world.nk - 1 << "\">\n";
// output data stored on nodes (point data)
out << "<PointData>\n";
// node volumes,scalar
out << "<DataArray Name=\"NodeVol\" NumberOfComponents=\"1\" format=\"ascii\" type=\"Float64\">\n";
out << world.node_vol; // use the overloaded << operator
out << "</DataArray>\n";
// potential,scalar
out << "<DataArray Name=\"phi\" NumberOfComponents=\"1\" format=\"ascii\" type=\"Float64\">\n";
out << world.phi; // use the overloaded << operator
out << "</DataArray>\n";
/* */ // output world.phi
// charge density,scalar
out << "<DataArray Name=\"rho\" NumberOfComponents=\"1\" format=\"ascii\" type=\"Float64\">\n";
out << world.rho; // use the overloaded << operator
out << "</DataArray>\n";
/* */ // output world.rho
// electric field,3 component vector
out << "<DataArray Name=\"ef\" NumberOfComponents=\"3\" format=\"ascii\" type=\"Float64\">\n";
out << world.ef; // uses overloaded << from Field_ and vec3
out << "</DataArray>\n";
// close the tags
out << "</PointData>\n";
out << "</ImageData>\n";
out << "</VTKFile>\n";
// species number densities
for (Species& sp : species) {
out << "<DataArray Name=\"nd." << sp.name << "\" NumberOfComponents=\"1\" format=\"ascii\" type=\"Float64\">\n";
out << sp.den;
out << "</DataArray>\n";
}
} // file closed here as 'out' goes out of scope
将带有错误的函数从 .cpp 移动到 .h 中的类解决了一个错误。但这对于其他错误来说是不可能的,因为需要上课才能将它们放入。
// Fields_.h
#pragma once
#include <ostream>
//#include <utility>
#include "vec3.h"
template <typename T>
class Field_{
public:
// constructor
Field_(int ni,int nj,int nk) : ni{ ni },nj{ nj },nk{ nk }{
data = new T * *[ni]; // ni pointers to pointers of type T
for (int i = 0; i < ni; i++) {
data[i] = new T * [nj]; // allocte nj pointers to T
for (int j = 0; j < nj; j++)
data[i][j] = new T[nk]; // allocate nk objects of type T
}
// when creating a scalar Field (not Field_<double3>),initialization has to be done explicitly
if (!std::is_same<T,double3>::value) {
operator=(0);
}
//operator=(0); // call the overloaded operator= function
//(*this) = 0; // clear data (doesn't work)
}
// destructor,frees momory in reverse order
~Field_() {
if (data == nullptr) return; // return if unallocated
for (int i = 0; i < ni; i++) { // release memory in reverse order
for (int j = 0; j < nj; j++)
delete data[i][j];
delete data[i];
}
delete[] data;
data = nullptr; // mark as free
}
// data acces operator
T** operator[] (int i) { return data[i]; }
// overload the assignment operator
Field_<T>& operator= (const T s) {
for (int i = 0; i < ni; i++)
for (int j = 0; j < nj; j++)
for (int k = 0; k < nk; k++)
data[i][j][k] = s;
return *this; // return refernce to self
}
// copy constructor
Field_(const Field_& other) :
Field_{ other.ni,other.nj,other.nk } {
for (int i = 0; i < ni; i++)
for (int j = 0; j < nj; j++)
for (int k = 0; k < nk; k++)
data[i][j][k] = other(i,j,k);
}
// move construtor
Field_(Field_ &&other) noexcept:
ni{ other.ni },nj{ other.nj },nk{ other.nk } {
if (data) this->~Field_(); // deallocate own data /*doesn't work??? why is it needed?*/
data = other.data; // steal the data
other.data = nullptr; // invalidate
}
// move assignment operator
Field_& operator=(Field_&& f) {
if (data) ~Field_(); // deallocate own data
data = f.data; f.data = nullptr; return *this;
}
// read-only acces to data[i][j][k]
T operator() (int i,int j,int k) const { return data[i][j][k]; }
void operator /=(const Field_& other) {
for (int i = 0; i < ni; i++)
for (int j = 0; j < nj; j++)
for (int k = 0; k < nk; k++) {
if (other.data[i][j][k] != 0)
data[i][j][k] /= other(i,k); // in the book data[i][j][k] /= other[i][j][k];
else
data[i][j][k] = 0;
}
}
Field_& operator += (const Field_& other) {
for (int i = 0; i < ni; i++)
for (int j = 0; j < nj; j++)
for (int k = 0; k < nk; k++)
data[i][j][k] += other(i,k);
return (*this);
}
// compound multiplication
Field_& operator *= (double s) {
for (int i = 0; i < ni; i++)
for (int j = 0; j < nj; j++)
for (int k = 0; k < nk; k++)
data[i][j][k] *= s;
return (*this);
}
// multiplikation operator,returns new Field set to f*s
friend Field_<T> operator*(double s,const Field_<T>& f) {
Field_<T> r(f);
return std::move(r *= s); // force move
//return move(r *= s); // force move
//return r;
//return r *= s;
}
void scatter(double3 lc,double value) {
// make sure we are in domain
if (lc[0]<0 || lc[0]>ni - 1 || lc[1]<0 || lc[1]>nj - 1 || lc[2]<0 || lc[2]>nk - 1) return;
// compute the cell index and the fractional distances
int i = (int)lc[0];
double di = lc[0] - i;
int j = (int)lc[1];
double dj = lc[1] - j;
int k = (int)lc[2];
double dk = lc[2] - k;
// deposit fractional values to the 8 surrounding nodes
data[i][j][k] += value * (1 - di) * (1 - dj) * (1 - dk);
data[i + 1][j][k] += value * (di) * (1 - dj) * (1 - dk);
data[i + 1][j + 1][k] += value * (di) * (dj) * (1 - dk);
data[i][j + 1][k] += value * (1 - di) * (dj) * (1 - dk);
data[i][j][k + 1] += value * (1 - di) * (1 - dj) * (dk);
data[i + 1][j][k + 1] += value * (di) * (1 - dj) * (dk);
data[i + 1][j + 1][k + 1] += value * (di) * (dj) * (dk);
data[i][j + 1][k + 1] += value * (1 - di) * (dj) * (dk);
}
friend std::ostream& operator<<(std::ostream& out,Field_<T>& f); // so data can be protected member of Field_
const int ni,nj,nk; // number of nodes
protected:
T*** data; // pointer of type T
};
template<typename T>
// output
std::ostream& operator<<(std::ostream& out,vec3<T>& v) {
out << v[0] << " " << v[1] << " " << v[2];
return out;
}
using Field = Field_<double>; // field of doubles
using FieldI = Field_<int>; // field of integers
using Field3 = Field_<double3>; // vector field of doubles
// Fields_.cpp
#include "Fields_.h"
解决方法
在 Fields_ 类中添加 operator<<
重载(和 scatter
函数)解决了这些错误。现在还有其他问题,但这似乎已解决。
(@john:也应该可以在课外定义模板。但我不知道在这个例子中如何,这似乎足以满足我的需要。如果有人知道一个更优雅的工作,我也很乐意尝试一下。)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。