【发布时间】:2021-04-30 07:36:38
【问题描述】:
我在处理一个稍大的项目时似乎遇到了链接器问题。 (我正在使用 Visual Studios 2019。我正在尝试从 Lubos Briedas “通过示例进行等离子模拟”重新创建代码,并且书中有一些错误,尽管其中大部分都很好地介绍了使用 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,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<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::vector<class Species,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<<-overloading 是否可以在类之外完成(就像在代码中一样),这似乎很好。将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, j, 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, j, 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"
【问题讨论】:
-
您可能只是未能编译包含缺失函数定义的代码。除了许多其他原因。
-
声明和定义不匹配是另一个很大的可能性。我注意到您的 operatorboth 是否没有 const(或两者都有)。
-
编译过程好像没问题,但是不知道怎么复查。我仔细检查了定义声明之间的不匹配,但找不到。我还尝试添加
const(两者都适用),但这没有帮助。所以我改回没有const的代码,因为原来的代码也没有(而且这个函数似乎没有调用右值)。 -
好的,很简单,模板必须在头文件中定义。
-
我尝试将整个
template<typename T> std::ostream ...移动到 Output.h 但它没有改变任何东西。 @john 你是这个意思吗?
标签: c++ operator-overloading linker-errors lnk2019 unresolved-external