【问题标题】:Do template specializations require template<> syntax?模板特化是否需要 template<> 语法?
【发布时间】:2009-06-01 22:22:36
【问题描述】:

我有一个类似这样的访问者类:

struct Visitor 
{
    template <typename T>
    void operator()(T t)
    {
        ...
    }

    void operator()(bool b)
    {
        ...
    }
};

显然,operator()(bool b) 旨在成为上述模板函数的特化。

但是,它没有我以前看到的 template&lt;&gt; 语法,将其声明为模板特化。但它确实可以编译。

这样安全吗?这是正确的吗?

【问题讨论】:

    标签: c++ templates syntax template-specialization


    【解决方案1】:

    您的代码不是模板特化,而是非模板函数。那里有一些差异。非模板化的 operator() 将优先于模板化的版本(为了完全匹配,但类型转换不会在那里发生),但您仍然可以强制调用模板化的函数:

    class Visitor
    {
    public: // corrected as pointed by stefanB, thanks
       template <typename T>
       void operator()( T data ) {
          std::cout << "generic template" << std::endl;
       }
       void operator()( bool data ) {
          std::cout << "regular member function" << std::endl;
       }
    };
    template <> // Corrected: specialization is a new definition, not a declaration, thanks again stefanB 
    void Visitor::operator()( int data ) {
       std::cout << "specialization" << std::endl;
    }
    int main()
    {
       Visitor v;
       v( 5 ); // specialization
       v( true ); // regular member function
       v.operator()<bool>( true ); // generic template even if there is a non-templated overload
       // operator() must be specified there (signature of the method) for the compiler to 
       //    detect what part is a template. You cannot use <> right after a variable name
    }
    

    在您的代码中并没有太大的区别,但是如果您的代码需要传递模板参数类型,它会变得更有趣:

    template <typename T>
    T g() { 
       return T();
    }
    template <>
    int g() {
       return 0;
    }
    int g() {
       return 1;
    }
    int main()
    {
       g<double>(); // return 0.0
       g<int>(); // return 0
       g(); // return 1 -- non-templated functions take precedence over templated ones
    }
    

    【讨论】:

    • 我认为顶部代码部分的“template void operator()( int data ) {”应该是“template void operator()( int data ) {”,在底部,“int g() {”应该是底部的“int g() {”(对不起,我不知道如何在 cmets 中对代码部分进行风格化)
    • 我有一个疑问,但是 GCC 和 Comeau 编译器都认为代码是有效的。我现在无法测试 MSVS,如果您可以尝试一下,我将不胜感激 :)
    • 还是在这里看看答案stackoverflow.com/questions/937744/…
    • 我有。我确实看过它并回到标准并用编译器重新测试。如果您正在为已经具有非模板签名的类型专门化模板,则需要额外的一对括号 。编译器将在特化点与两个签名混淆(将尝试非模板并且无法特化非模板化的成员函数)。
    • @James。编译器能够通过查看参数类型来推断显式特化的“T”,因此在这种情况下没有必要(除了作为样式偏好)显式指定模板参数。在这种情况下,实际上存在一个允许从返回类型中扣除的核心问题,这通常是不可能的:open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#621
    【解决方案2】:

    您在这里拥有的是函数重载;要获得模板专业化,您确实需要template &lt;&gt; 语法。但是,您应该知道这两种方法,即使它们看起来相同,也有细微的不同,甚至编译器在选择要调用的正确函数时也可能会迷失方向。列出所有可能的情况对于这个答案来说有点太长了,但你可能想检查Herb Sutter GoTW #49这个主题。

    【讨论】:

      【解决方案3】:

      哦,它会编译。它只是不会是模板功能。您将拥有一个常规的非模板函数,而不是模板特化。

      它很安全,实际上也可能是您想要的。访问者模式通常通过重载来实现。特化函数模板isn't really a good idea anyway.

      【讨论】:

      • 是否存在表现不同的情况?这是一个潜在的错误,还是只是糟糕的风格?
      • 通常,这是正确的做法。您通常应该更喜欢普通重载而不是函数模板特化。行为更容易预测。
      【解决方案4】:

      你做的不是模板序列化,而是函数重载。很安全。

      附:很难说它是否正确,不知道你想要实现什么。请记住,无论是模板还是重载函数,都会在编译时选择您的运算符。如果需要运行时分派,则需要多态性,而不是重载。好吧,无论如何您可能都知道;以防万一。

      【讨论】:

        【解决方案5】:

        你有

        • void operator()(bool b) 非 模板化函数
        • template< typename T > void operator()(T t) 这是一个单独的 重载的基本模板 以上

        您可以像 template&lt;&gt; void operator(int i) 那样对第二个进行完全专业化,只有在 void operator()(bool b) 不匹配时才会考虑。

        基础模板的特化用于选择调用哪个基础模板方法。但是,在您的情况下,您有一个将首先考虑的非模板化方法。

        Why Not Specialize Function Templates? 文章很好地解释了如何选择方法。

        总结:

        1. 非模板函数是 首先考虑(这是你的平原 上面的 operator()(bool))
        2. 检查功能基础模板 第二(这是你的模板 函数),选择最特化的基本模板,然后如果它对使用特化的确切类型有特化,否则基本模板与“正确”类型一起使用(参见文章中的解释)

        例子:

        #include <iostream>
        using namespace std;
        
        struct doh
        {
            void operator()(bool b)
            {
                cout << "operator()(bool b)" << endl;
            }
        
            template< typename T > void operator()(T t)
            {
                cout << "template <typename T> void operator()(T t)" << endl;
            }
        };
        // note can't specialize inline, have to declare outside of the class body
        template<> void doh::operator()<>(int i)
        {
            cout << "template <> void operator()<>(int i)" << endl;
        }
        template<> void doh::operator()<>(bool b)
        {
            cout << "template <> void operator()<>(bool b)" << endl;
        }
        
        int main()
        {
            doh d;
            int i;
            bool b;
            d(b);
            d(i);
        }
        

        你接到电话:

        operator()(bool b)       <-- first non template method that matches
        template <> void operator()(int i)     <-- the most specialized specialization of templated function is called
        

        【讨论】:

        • 你错过了参数括号: template void doh::operator()(bool b) (注意我插入的“”),并注意他(sutter)明确写道选择最专业的基本模板。不是“最匹配的专业化”也不是“模板化函数最专业化的专业化”。该决议明确地只考虑主要(基本)模板。这称为“函数模板的部分排序”,在标准的 14.6.6.2 中有描述。
        • 关于基本模板的选择你是对的,我想我把它短路了我的解释(懒惰打字?),我想说选择最专业的基本模板,然后如果它对使用专门化的确切类型进行专门化,否则基本模板与“正确”类型一起使用
        • 在 gcc3.3.3 上,带有括号 template void doh::operator()(bool b) 和没有表单模板 void doh::operator()(bool b) 编译和给了我相同的结果,仍然复制/粘贴@dribeas 的示例会给出错误错误:非命名空间范围内的显式专业化 `struct doh' 等等 - 这就是我所指的(并且因为方法都是私人的,但这是旁注)
        • 我修正了解释(我希望)
        • 你能解释一下第二个括号 in: template void doh::operator()(bool b) 背后的原因吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-09
        • 2018-07-02
        • 2013-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多