#include <QFile>
#include <QString>

// this is some sort of low-level C function
void lowLevelOpenFD(int fd)
{
    qDebug("Opened by fd: %d", fd);
}

// as well as this one
void lowLevelOpenName(const char *name)
{
    qDebug("Opened by name: %s", name);
}

// this is a wrapper around low-level functions
template<typename FileId>
void callLowLevelOpen(FileId id);

template<>
void callLowLevelOpen(const QString &fileName)
{
    lowLevelOpenName(QFile::encodeName(fileName).constData());
}

template<>
void callLowLevelOpen(int fd)
{
    lowLevelOpenFD(fd);
}

// this is the function where the most stuff happens
template<typename FileId>
void openInternal(FileId id)
{
    // lots of useful stuff goes here
    // now we call one of the two low level functions
    callLowLevelOpen(id);
    // more useful stuff
}

// this is high-level interface to the "open by name" function
void openFile()
{
    QString name = "file";
    openInternal(name);
}

// this is high-level interface to the "open by FD" function
void openFile(int fd)
{
    openInternal(fd);
}

int main()
{
    openFile();
    openFile(17);
    return 0;
}

问题是上面的示例导致
error LNK2019: unresolved external symbol "void __cdecl callLowLevelOpen<class QString>(class QString)" (??$callLowLevelOpen@VQString@@@@YAXVQString@@@Z) referenced in function "void __cdecl openInternal<class QString>(class QString)" (??$openInternal@VQString@@@@YAXVQString@@@Z)

据我所知,这是因为编译器在从第一个高级重载中调用openInternal<QString>()时实例化了它。好的,所以我想到并修改了代码:
// this is high-level interface to the "open by name" function
void openFile()
{
    QString name = "file";
    openInternal<const QString&>(name);
}

同样的问题。我以为我告诉编译器实例化openInternal<const QString&>,那么为什么它仍然抱怨<class QString>?我也尝试过这个:
// this is high-level interface to the "open by name" function
void openFile()
{
    QString name = "file";
    openInternal<const QString&>(static_cast<const QString&>(name));
}

现在,这看起来很愚蠢,但仍然无法正常工作。我不能明确地专门化openInternal(),因为它太大了,这种模板化困惑的关键是避免不必要的代码重复。我不能只重命名低级函数以将它们转换为重载函数,因为它们在第三方C库中。我唯一能做的就是将第一个callLowLevelOpen()特化替换为
template<>
void callLowLevelOpen(QString fileName)
{
    lowLevelOpenName(QFile::encodeName(fileName).constData());
}

然后就可以了。实际上,性能损失几乎为零,因此这是一个完全有效的解决方法,但我只想了解这里发生的情况。

上面的代码只是一个SSCCE,如果有人感兴趣,实际的代码是there。这个特殊问题与gzopen()/gzdopen()QuaGzipFilePrivate::open()QuaGzipFile::open()函数有关。

最佳答案

由于您实际上更改了签名,因此我认为您实际上不想专门化函数模板,而是使用重载。这是使用std::string的完整测试程序(如果可以在此重现问题,我不知何故只希望依赖于标准类):

#include <string>

template <typename T> void f(T);
// #define BROKEN
#if defined(BROKEN)
template <> void f(std::string const&) {}
#else
void f(std::string const&) {}
#endif

int main()
{
    std::string s;
    f(s);
}

如果您在此代码中使用#defined BROKEN,它将无法正常工作。

出现这种情况的原因是,编译器根据主模板选择重载。这将永远不会添加const&部分。完成此操作后,编译器将寻找所选重载的潜在特长。由于这永远不会推导用于特化的符号,因此不会被采用。

为何f<std::string const&>(s)没有选择专业?对我来说,尝试同时使用gcc和clang。

关于c++ - const QString&的显式模板特化导致 Unresolved external ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9054942/

10-13 08:24