我试图向我的 class 添加一个新的模板辅助函数,但现在我收到了 LNK2001 错误。怎么修?

I tried to add a new template helper function to my class and now I get a LNK2001 error. How to fix?

我最近在 Whosebug 上问了几个问题,看看我是否可以通过使用模板将一些功能合并为一个。这些问题是:

我还有一个功能要尝试更新,所以我想我会自己试一试。

这是要更新的函数:

void CMSATools::ConvertSAFEARRAY_DATE(SAFEARRAY* psaDates, MeetingDates& rMapMeetingDates)
{
    DATE *pVals = nullptr;
    HRESULT hr = SafeArrayAccessData(psaDates, (void**)&pVals); // direct access to SA memory

    if (SUCCEEDED(hr))
    {
        long lowerBound, upperBound;  // get array bounds
        hr = SafeArrayGetLBound(psaDates, 1, &lowerBound);
        if (FAILED(hr))
            throw _com_error(hr);

        hr = SafeArrayGetUBound(psaDates, 1, &upperBound);
        if (FAILED(hr))
            throw _com_error(hr);

        long cnt_elements = upperBound - lowerBound + 1;
        for (int i = 0; i < cnt_elements; ++i)  // iterate through returned values
        {
            COleDateTime datNotAvailable(pVals[i]);
            DWORD dwDatNotAvailable = EncodeMeetingDate(0, datNotAvailable);
            rMapMeetingDates[dwDatNotAvailable] = datNotAvailable;
        }
        hr = SafeArrayUnaccessData(psaDates);
        if (FAILED(hr))
            throw _com_error(hr);
    }
    else
    {
        throw _com_error(hr);
    }

    hr = SafeArrayDestroy(psaDates);
    if (FAILED(hr))
        throw _com_error(hr);
}

MeetingDates定义如下:

 using MeetingDates = std::map<DWORD, COleDateTime>;

所以我创建了这个辅助函数:

template<>
void CMSATools::to_push_back(const DATE& rItem, MeetingDates& rItems)
{
    COleDateTime datNotAvailable(rItem);
    DWORD dwDatNotAvailable = EncodeMeetingDate(0, datNotAvailable);
    rItems[dwDatNotAvailable] = datNotAvailable;
}

然后我这样调整了我的调用代码:

theApp.MSAToolsInterface().ConvertSAFEARRAY<DATE,MeetingDates>(psaDates, mapMeetingDates);

但是当我编译这个时,我现在得到一个错误:

5>PublishersDatabaseDlg.obj : error LNK2001: unresolved external symbol "public: static void __cdecl CMSATools::ConvertSAFEARRAY<double,class std::map<unsigned long,class ATL::COleDateTime,struct std::less,class std::allocator<struct std::pair<unsigned long const ,class ATL::COleDateTime> > > >(struct tagSAFEARRAY *,class std::map<unsigned long,class ATL::COleDateTime,struct std::less,class std::allocator<struct std::pair<unsigned long const ,class ATL::COleDateTime> > > &)" (??$ConvertSAFEARRAY@NV?$map@KVCOleDateTime@ATL@@U?$less@K@std@@V?$allocator@U?$pair@$$CBKVCOleDateTime@ATL@@@std@@@4@@std@@@CMSATools@@SAXPAUtagSAFEARRAY@@AAV?$map@KVCOleDateTime@ATL@@U?$less@K@std@@V?$allocator@U?$pair@$$CBKVCOleDateTime@ATL@@@std@@@4@@std@@@Z)

我做错了什么?


到目前为止,我的代码在我进行 DATE... 模板化调用时无法编译:

template<typename to>
void CMSATools::to_clear(to& rItems)
{
    rItems.clear();
}

template<typename from, typename to>
void CMSATools::to_push_back(const from& rItem, to& rItems)
{
    rItems.push_back(rItem);
}

template<>
void CMSATools::to_clear(CStringArray& rItems)
{
    rItems.RemoveAll();
}

template<>
void CMSATools::to_push_back(const BSTR& rItem, CStringArray& rItems)
{
    rItems.Add(rItem);
}

template<>
void CMSATools::to_push_back(const DATE& rItem, MeetingDates& rItems)
{
    COleDateTime datNotAvailable(rItem);
    DWORD dwDatNotAvailable = EncodeMeetingDate(0, datNotAvailable);
    rItems[dwDatNotAvailable] = datNotAvailable;
}

template<typename from, typename to>
void CMSATools::ConvertSAFEARRAY(SAFEARRAY* psaItems, to& rItems)
{
    from* pVals = nullptr;
    HRESULT hr = SafeArrayAccessData(psaItems, (void**)&pVals); // direct access to SA memory

    if (SUCCEEDED(hr))
    {
        long lowerBound, upperBound;  // get array bounds
        hr = SafeArrayGetLBound(psaItems, 1, &lowerBound);
        if (FAILED(hr))
            throw _com_error(hr);

        hr = SafeArrayGetUBound(psaItems, 1, &upperBound);
        if (FAILED(hr))
            throw _com_error(hr);

        to_clear<to>(rItems);
        long cnt_elements = upperBound - lowerBound + 1;
        for (int i = 0; i < cnt_elements; ++i)  // iterate through returned values
        {
            to_push_back<from, to>(pVals[i], rItems);
        }
        hr = SafeArrayUnaccessData(psaItems);
        if (FAILED(hr))
            throw _com_error(hr);
    }
    else
    {
        throw _com_error(hr);
    }

    hr = SafeArrayDestroy(psaItems);
    if (FAILED(hr))
        throw _com_error(hr);
}

我这是对类似标题问题的回答:

而且我认为它可能适用于我的情况,但我不确定如何实施它。如果确实是这个原因。


我的 header class 中有这个片段:


template<typename to>
static void to_clear(to& rItems);
template<typename from, typename to>
static void to_push_back(const from& rItem, to& rItems);
template<>
static void to_clear(CStringArray& rItems);
template<>
static void to_push_back(const BSTR& rItem, CStringArray& rItems);
template<>
static void to_push_back(const DATE& rItem, MeetingDates& rItems);
template<typename from, typename to>
static void ConvertSAFEARRAY(SAFEARRAY* psaItems, to& rItems);
static DWORD EncodeMeetingDate(int iMeetingType, COleDateTime datMeeting);

在我的 header 文件的顶部,我有:

#pragma once
#include "DemoPickerDlg.h"
#include <map>
#include <vector>

#ifdef _WIN64
#import "..\..\MSAToolsLibrary\MSAToolsLibrary\bin\x64\Release\MSAToolsLibrary.tlb" raw_interfaces_only named_guids
#else
#import "..\..\MSAToolsLibrary\MSAToolsLibrary\bin\x86\Release\MSAToolsLibrary.tlb" raw_interfaces_only named_guids
#endif

using MeetingDates = std::map<DWORD, COleDateTime>;
using ListDiscussionItems = std::list<MSAToolsLibrary::IDiscussionItemPtr>;
using ListStudentItems = std::list<MSAToolsLibrary::IStudentItemPtr>;
using ListDutyHistoryLookupItems = std::list<MSAToolsLibrary::IDutyAssignmentLookupPtr>;

评论中提供了一个链接问题 (Why can templates only be implemented in the header file?)。但答案本身并没有让我清楚。 它链接到 的文章帮助我理解了:

引用:

But in order to understand why things are the way they are, first accept these facts:

  1. A template is not a class or a function. A template is a “pattern” that the compiler uses to generate a family of classes or functions.

这让我明白了。我将模式代码移到了 header 中,现在可以正常编译了。我发现这是最简单的解决方案。