【问题标题】:Template specialization fails on linking链接时模板专业化失败
【发布时间】:2011-10-06 00:07:58
【问题描述】:

我想了解模板专业化的问题。我正在使用 Visual C++ 10.0 (2010)。我有这样的课:

class VariableManager
{
public:

    template<typename VarT>
        VarT get(std::string const& name) const
        {
                    // Some code...
        }

   // This method supposed to be fully evaluated, linkable method.   
    template<>
        std::string get<std::string>(std::string const& name) const;

private:
    std::map<std::string, boost::any> mVariables;
};

理论上,因为我专门使用了“get”方法,所以链接器应该能够从目标文件中获取。相反,如果我将方法放在源文件中,则链接器会出现未解决的引用错误:

    template<>
    std::string VariableManager::get<std::string>(std::string const& name) const
            {
                 // Doing something...
            }

如果我将此方法作为内联方法放在头文件中,则构建会正常进行。我确实理解模板的功能是这样的:

        template<typename VarT>
            VarT get(std::string const& name) const;

应该放在头文件中,因为编译器不能根据调用代码特化模板,但在完全特化的情况下,是类的实现,因此特化的模板方法应该已经作为公共符号存在。有人可以解释一下这个问题吗?

【问题讨论】:

    标签: c++ templates visual-c++


    【解决方案1】:

    您的分析是正确的 - 一个显式专门化的函数模板,它具有使用显式值指定的任何模板参数,提供了函数的完整定义。

    如果您已将包含显式特化定义的相应.cpp 文件正确包含到您的项目中,则 VC++ 不应引发链接器错误。不过,为了符合标准,请注意,您必须在封闭类之外声明您的专业化。该标准禁止在封闭类内部声明显式特化(其他编译器将拒绝您的代码)。所以更改头文件以像这样声明特化,而不是

    class VariableManager
    {
    public:
    
        template<typename VarT>
            VarT get(std::string const& name) const
            {
                        // Some code...
            }
    
    private:
        std::map<std::string, boost::any> mVariables;
    };
    
    // This method supposed to be fully evaluated, linkable method.   
    template<>
    std::string VariableManager::get<std::string>(std::string const& name) const;
    

    我还要注意,你不能在你的类体内调用get&lt;std::string&gt;。这是因为任何这样的调用都不会看到显式的特化声明,因此会尝试从模板定义中实例化函数。标准使此类代码格式错误,无需进行诊断。

    【讨论】:

    • 很有趣,但似乎标准合规问题才是问题所在。我从类中删除了声明,并按照您的建议进行了专业化声明,链接器没有问题。最好记住,必须在类的范围之外声明特化。感谢您的热心帮助!
    • 我真的很幸运能找到你的答案。如果我将模板特化留在类中,编译器会做什么,以及是什么让链接器无法找到它,这对我来说仍然是个谜。但是,你绝对拯救了我的一天!
    【解决方案2】:

    专门化模板不会强制编译器实例化它(我认为 GCC 会这样做);您仍然需要明确告诉编译器实际实例化模板。您可以通过显式模板实例化来做到这一点。基本上,只需在源文件中添加:

    template std::string VariableManager::get(const std::string& name) const;
    

    【讨论】:

    • 错了。他对它进行了专门化,因此他不需要也不想实例化模板
    • litb 是对的,这确实是错的,但我会在这里留下我的答案,以便其他人也能看到它是错误的
    【解决方案3】:

    template&lt;&gt; 开头的方法仍被视为template 专业化 方法。所以你必须要放入一个头文件中。

    如果你想把它放到一个实现文件中,那么你必须重载它。

    class VariableManager
    {
    //...
      VarT get(std::string const& name) const
      {}
    
      std::string get(std::string const& name) const; //overloading not specialization
    };
    

    【讨论】:

    • 不,你没有。请参阅reko的答案。而且我不确定您所说的“模板专业化方法”是什么意思。
    • 但是我建议重载而不是专业化任何一天!)
    • @Tomalak,OP 正试图在问题中专门化 template 方法 get。我的回答是,应该在头文件中声明专门的方法。你能详细说明一下,有什么问题吗?
    • @iammilind:见 litb 的回答,他解释得很好。他所做的模板专业化的问题是因为实际上不允许这样做。显式特化必须在命名空间范围内完成,但由于某种原因,MSVC 不会对类范围内的特化给出错误。如果他做得正确,即使代码不在头文件中,它也可以正常工作。这就是您的回答有问题的地方;您不需要在标题中包含代码,因为显式特化并不是真正的模板,只是一个常规函数。
    猜你喜欢
    • 1970-01-01
    • 2020-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-24
    • 1970-01-01
    • 2018-09-28
    相关资源
    最近更新 更多