为什么这个 c++ 代码不能在 g++ 4.8.2 中编译

Why does this c++ code not compile in g++ 4.8.2

本文关键字:编译 g++ c++ 代码 不能 为什么      更新时间:2023-10-16

>我正在尝试使用 g++ 4.8.2(Ubuntu 14.04 环境)编译C++脉冲国际象棋引擎 [0]。

我在链接阶段收到以下错误:

Linking CXX executable pulse-
CMakeFiles/pulse.dir/main.cpp.o: In function `pulse::MoveGenerator::MoveGenerator()':
/home/user/cpp/movegenerator.h:15: undefined reference to `pulse::MoveList<pulse::MoveEntry>::MoveList()'
libcore.so: undefined reference to `pulse::MoveList<pulse::MoveEntry>::rateFromMVVLVA()'
libcore.so: undefined reference to `pulse::MoveList<pulse::RootEntry>::sort()'
libcore.so: undefined reference to `pulse::MoveList<pulse::RootEntry>::MoveList()'
libcore.so: undefined reference to `pulse::MoveList<pulse::MoveEntry>::sort()'
collect2: error: ld returned 1 exit status

当我运行命令时:

g++ -std=c++11 -c movelist.cpp && nm movelist.o | grep sort

我得到输出

0000000000000000 W _ZN5pulse8MoveListINS_9MoveEntryEE4sortEv
0000000000000000 W _ZN5pulse8MoveListINS_9RootEntryEE4sortEv
使用

4.9.1 时,但使用 4.8.2 时没有输出。

为什么 g++ 4.9.1 生成排序函数而 4.8.2 没有,是编译器中的错误还是脉冲源代码中的错误?

编辑:

正如指出的重复问题"为什么模板只能在头文件中实现?"中所述,我不明白,它在 g++ 4.9.1 中是如何工作的?移动列表标头:

#include "value.h"
#include "move.h"
#include <array>
#include <memory>
namespace pulse {
/**
 * This class stores our moves for a specific position. For the root node we
 * will populate pv for every root move.
 */
template<class T> 
class MoveList {
private:
  static const int MAX_MOVES = 256;
public:
  std::array<std::shared_ptr<T>, MAX_MOVES> entries;
  int size = 0; 
  MoveList();
  void sort();
  void rateFromMVVLVA();
};
class MoveVariation {
public:
  std::array<int, Depth::MAX_PLY> moves;
  int size = 0; 
};
class MoveEntry {
public:
  int move = Move::NOMOVE;
  int value = Value::NOVALUE;
};
class RootEntry : public MoveEntry {
public:
  MoveVariation pv;
};
}

移动列表源:

#include "movelist.h"
#include <cassert>
namespace pulse {
template class MoveList<MoveEntry>;
template class MoveList<RootEntry>;
template<class T>
MoveList<T>::MoveList() {
  for (unsigned int i = 0; i < entries.size(); ++i) {
    entries[i] = std::shared_ptr<T>(new T());
  }  
}
/**
 * Sorts the move list using a stable insertion sort.
 */
template<class T> 
void MoveList<T>::sort() {
  for (int i = 1; i < size; ++i) {
    std::shared_ptr<T> entry(entries[i]);
    int j = i; 
    while ((j > 0) && (entries[j - 1]->value < entry->value)) {
      entries[j] = entries[j - 1];
      --j;
    }  
    entries[j] = entry;
  }  
}
/**
 * Rates the moves in the list according to "Most Valuable Victim - Least Valuable Aggressor".
 */
template<class T> 
void MoveList<T>::rateFromMVVLVA() {
  for (int i = 0; i < size; ++i) {
    int move = entries[i]->move;
    int value = 0; 
    int piecetypeValue = PieceType::getValue(Piece::getType(Move::getOriginPiece(move)));
    value += PieceType::KING_VALUE / piecetypeValue;
    int target = Move::getTargetPiece(move);
    if (Piece::isValid(target)) {
      value += 10 * PieceType::getValue(Piece::getType(target));
    }
    assert(value >= (PieceType::KING_VALUE / PieceType::KING_VALUE)
      && value <= (PieceType::KING_VALUE / PieceType::PAWN_VALUE) + 10 * PieceType::QUEEN_VALUE);
    entries[i]->value = value;
  }
}

[0] https://github.com/fluxroot/pulse/tree/master/src/main/cpp

解决方案

将以模板类开头的两行移动到源文件的末尾。

解释

此源使用显式模板实例化 (14.7.2)。在源文件(而不是标头中)定义模板是完全可以的,只要在整个程序中仅使用模板的显式实例化即可。在这种情况下,这些将是 MoveList<MoveEntry>MoveList<RootEntry> .

但是,存在一个问题:显式专用化必须出现在模板定义之后 (14.7.2/4)

函数模板、变量模板、类模板的成员函数或

静态数据成员,或类或类模板的成员函数模板的声明应先于该实体的显式实例化。类模板的定义、类模板的成员类或类或类模板的成员类模板应先于该实体的显式实例化,除非显式实例化之前具有相同模板参数的实体的显式专用化。

为什么 g++ 4.9 没有强制执行这一点是任何人的猜测。