【问题标题】:MSVC compiler instantiates a function template's default definition even though a specialization exists即使存在特化,MSVC 编译器也会实例化函数模板的默认定义
【发布时间】:2019-01-15 14:23:08
【问题描述】:

以下代码在使用 Clang 的 macOS 上编译和运行正常,但在使用 MSVC 2017 的 Windows 上却没有。

// File: toString.h
#include <string>
template<typename T>
const std::string toString(T){return "";}

// File: toString.cpp
#include "toString.h"
template <>
const std::string toString<int>(int value){
    return std::to_string(value);
}

// File: main.cpp
#include <iostream>
#include "toString.h"

int main() {
    // specialized
    std::cout <<"int: "<< toString(1) << std::endl;
    // not specialized
    std::cout <<"double: "<< toString(1.0) << std::endl;
    return 0;
}

// Expected output:
// int: 1
// double: 

链接器失败,因为函数被隐式实例化而不是链接到 int 特化,导致重复符号。

如果模板的默认实现被删除,那么打印double 的行将会失败,因为没有符号可以链接到它。

我的问题是是否有任何方法可以在使用 MSVC 的 Windows 上实现相同的结果,而 main.cpp 没有任何 toString 专业化(声明或定义)的可见性。

如果没有,这是否包含在标准中或仅仅是编译器实现细节?

【问题讨论】:

    标签: c++ visual-c++ template-specialization


    【解决方案1】:

    tostring.h 中没有任何内容可以告诉编译器您的专业化存在。因此,在编译main.cpp 时,编译器只需实例化头文件中声明的模板。违反了一个定义规则,因此行为未定义。它在 clang 中按您期望的那样工作,主要是由于链接时两个可用定义的运气,clang 选择了您想要的那个。

    要修复它,您需要在标头中前向声明您的特化,以便编译器知道在编译 main.cpp 时不要实例化模板:

    //toString.h
    #include <string>
    template<typename T>
    const std::string toString( T ) { return ""; }
    
    template <>
    const std::string toString<int>( int value );
    

    【讨论】:

    • 是的,这确实有效。我的想法是从图书馆的角度来看的。如果该库专门化了某些类型,而该库的用户专门化了某些其他类型,那么第三个用户将需要包含前两个的所有专门化声明。那好吧。我在这里找到了更多信息:stackoverflow.com/questions/31742318/…。一些编译器在编译期间实例化模板,而其他编译器在链接期间这样做。后者允许上述工作,而前者不允许。
    • 或者,仅在头文件中声明模板化函数,所有定义toString.cpp中。这可能是“编译器定义的 UB”,我没有检查,但我在几个必须在 MSVC 和 GCC 下编译的项目中使用它。
    • @pandorafalters 不,应该可以正常工作,只是意味着您必须显式实例化每种可能很乏味的类型
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-10
    • 2020-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多