C++ 模板函数的显式实例化导致错误 "no definition available"

C++ Explicit instantiation of a template function results in an error "no definition available"

尝试编译以下文件时出现此错误:

错误

Logging.h: In instantiation of 'void Sudoku::printBoardWithCandidates(const Sudoku::Board<BASE>&) [with int BASE = 3]':
Logging.h:10:64:   required from here
Logging.h:10:64: error: explicit instantiation of 'void Sudoku::printBoardWithCandidates(const Sudoku::Board<BASE>&) [with int BASE = 3]' but no definition available [-fpermissive]
     template void printBoardWithCandidates<3>(const Board<3>& b);
                                                                ^

由于我对 C++ 的经验不是很丰富,所以我看不出有任何可能导致此问题的原因。由于定义存在于 .cpp 文件中,我看不出有任何理由不编译。有人不介意给我解释一下吗?

.h 文件

#pragma once
#include "Board.h"
namespace Sudoku
{
    template<int BASE>
    void printBoardWithCandidates(const Board<BASE> &b);


    template void printBoardWithCandidates<2>(const Board<2>&);
    template void printBoardWithCandidates<3>(const Board<3>&);
    template void printBoardWithCandidates<4>(const Board<4>&);
} 

.cpp 文件

#include "Logging.h"
#include <iostream>

template<int BASE>
void Sudoku::printBoardWithCandidates(const Board<BASE> &board)
{
    // definition...
}

编辑: 我在整个程序中多次使用了类似的实现。例如 Board.h

#pragma once
#include <vector>
#include <cstring>
#include "stdint.h"

namespace Sudoku
{
    struct Cell
    {
        int RowInd;
        int ColInd;
        int BoxInd;

        friend bool operator==(const Cell& cell1, const Cell& cell2);
    };
    
    bool operator==(const Cell& cell1, const Cell& cell2);


    template<int BASE>
    class Board
    {
        public:
            static constexpr int WIDTH = BASE * BASE;
            static constexpr int CELL_COUNT = WIDTH * WIDTH;
            static constexpr uint16_t CELL_COMPLETELY_OCCUPIED = 65535 >> (sizeof(uint16_t) * 8 - WIDTH);

        private:   
            const int EMPTY_VALUE;
            
            uint16_t rowOccupants[WIDTH] = {0};
            uint16_t colOccupants[WIDTH] = {0};
            uint16_t boxOccupants[WIDTH] = {0};

            int* solution;
            void Init();
            void Eliminate(Cell& cell, uint16_t value);

        public:


            std::vector<Cell> EmptyCells;
            Board(const int* puzzle, int* solution, int emptyValue = -1);
            void SetValue(Cell cell, uint16_t value);
            void SetValue(Cell cell, int value);
            int* GetSolution() const; 
            inline uint16_t GetOccupants(Cell cell) const
            {
                return rowOccupants[cell.RowInd] | colOccupants[cell.ColInd] | boxOccupants[cell.BoxInd];
            }

    };
    template class Board<2>;
    template class Board<3>;
    template class Board<4>;
} // namespace Sudoku

问题 是在 header 文件中您提供 3 显式模板实例化的地方,对应的成员函数模板printBoardWithCandidates定义不可用。因此,编译器无法为这些实例生成定义,并给出上述错误:

error: explicit instantiation of 'void Sudoku::printBoardWithCandidates(const Sudoku::Board<BASE>&) [with int BASE = 3]' 
but no definition available [-fpermissive]
^^^^^^^^^^^^^^^^^^^^^^^^^^^

有两种方法可以解决这个问题,如下所示。另请注意,我使用了一个空结构 Board,因为您提供的 Board class 非常大(长度),如果粘贴到此处会占用很多 space 2次。不过概念是一样的。

方法一

在header中提供成员函数模板printBoardWithCandidates的定义,然后提供3个显式模板实例化,如下所示:

header.h

#pragma once
#include "Board.h"
namespace Sudoku
{
    template<int BASE>
    void printBoardWithCandidates(const Board<BASE> &b)
    {
        //note this is a definition
    }

    //explicit template instantiation declaration
    extern template void printBoardWithCandidates<2>(const Board<2>&);
    extern template void printBoardWithCandidates<3>(const Board<3>&);
    extern template void printBoardWithCandidates<4>(const Board<4>&);
} 

Board.h

#pragma once
template<int>
struct Board 
{
    
};

main.cpp


#include <iostream>

#include "header.h"
//explicit template instantiation definition
template void Sudoku::printBoardWithCandidates<2>(const Board<2>&);
template void Sudoku::printBoardWithCandidates<3>(const Board<3>&);
template void Sudoku::printBoardWithCandidates<4>(const Board<4>&);
int main()
{
    
    return 0;
}

Working demo


方法二

这里我们提供了成员函数模板的定义以及源文件中的3个显式模板实例化。在header文件中我们只提供了成员函数模板的声明,在header文件中没有提供显式的模板实例化。

header.h

#pragma once
#include "Board.h"
namespace Sudoku
{
    //this is a declaration not a definition
    template<int BASE>
    void printBoardWithCandidates(const Board<BASE> &b);


   //no explicit template instantiation here since we have provided only the declaration above and not the definition
} 

Board.h

#pragma once
template<int>
struct Board 
{
    
};

source.cpp

#include "header.h"

//provide the definition here
template<int BASE>
void Sudoku::printBoardWithCandidates(const Board<BASE> &board)
{
    // definition...
}
 template void Sudoku::printBoardWithCandidates<2>(const Board<2>&);
    template void Sudoku::printBoardWithCandidates<3>(const Board<3>&);
    template void Sudoku::printBoardWithCandidates<4>(const Board<4>&);

main.cpp

#include <iostream>

#include "header.h"

int main()
{
    
    return 0;
}

Working demo