【问题标题】:C++ syntax for explicit specialization of a template function in a template class?模板类中模板函数的显式特化的 C++ 语法?
【发布时间】:2011-01-07 01:20:44
【问题描述】:

我的代码在 VC9 (Microsoft Visual C++ 2008 SP1) 中有效,但在 GCC 4.2 (Mac 上) 中无效:

struct tag {};

template< typename T >
struct C
{   
    template< typename Tag >
    void f( T );                 // declaration only

    template<>
    inline void f< tag >( T ) {} // ERROR: explicit specialization in
};                               // non-namespace scope 'structC<T>'

我了解 GCC 希望我将我的显式专业化移到课堂之外,但我无法弄清楚语法。有什么想法吗?

// the following is not correct syntax, what is?
template< typename T >
template<>
inline void C< T >::f< tag >( T ) {}

【问题讨论】:

    标签: c++ templates gcc


    【解决方案1】:

    如果不明确特化包含类,就无法特化成员函数。
    但是,您可以做的是将调用转发到部分专用类型的成员函数:

    template<class T, class Tag>
    struct helper {
        static void f(T);   
    };
    
    template<class T>
    struct helper<T, tag1> {
        static void f(T) {}
    };
    
    template<class T>
    struct C {
        // ...
        template<class Tag>
        void foo(T t) {
            helper<T, Tag>::f(t);
        }
    };
    

    【讨论】:

    • 这与我使用的解决方案类似,谢谢!我想知道是否有办法使用 Boost 来减少冗长?
    • 并非如此,一般来说它不会变得更容易。您需要一个辅助函数来避免“封闭类必须显式特化”的问题,并且您需要将它移到一个类中以进行部分特化。
    【解决方案2】:

    GCC 是明确的,在这里。 MSVC 有一个非标准扩展,允许进行类内专业化。然而,该标准说:

    14.7.3.2:
    2. 应在命名空间中声明显式特化 模板是成员,或者,对于成员模板,在 封闭类或封闭类模板所在的命名空间 成员。成员函数的显式特化,member 类模板的类或静态数据成员应在 类模板所属的命名空间。

    此外,您不能对函数进行部分特化。 (虽然我不确定你案件的细节,但那将是最后一击。)

    可以这样做:

    #include <iostream>
    
    struct true_type {};
    struct false_type {};
    
    template <typename T, typename U>
    struct is_same : false_type
    {
        static const bool value = false;
    };
    
    template <typename T>
    struct is_same<T, T> : true_type
    {
        static const bool value = true;
    };
    
    struct tag1 {};
    struct tag2 {};
    
    template< typename T >
    struct C
    {
        typedef T t_type;
    
        template< typename Tag >
        void foo( t_type pX)
        {
            foo_detail( pX, is_same<Tag, tag1>() );
        }
    
    private:
        void foo_detail( t_type, const true_type& )
        {
            std::cout << "In tag1 version." << std::endl;
        }
        void foo_detail( t_type, const false_type& )
        {
            std::cout << "In not tag1 version." << std::endl;
        }
    };
    
    int main(void)
    {
        C<int> c;
        c.foo<tag1>(int());
        c.foo<tag2>(int());
        c.foo<double>(int());
    }
    

    虽然这有点难看。

    【讨论】:

    • 可能是 C++ 标准不清楚……如果微软能做到,为什么其他编译器不能?
    • 需要指出的是,自从回答了这个问题后,C++11 就出来了std::is_same&lt;&gt;,它替换了上面的部分代码。
    【解决方案3】:

    遇到了这个问题。这应该有效:

    struct tag {};
    
    template< typename T >
    struct C {   
        template< typename Tag, typename std::enable_if<std::is_same<Tag, tag>::value, int>::type = 0>
        void f( T ){
            std::cout<<"tag type" <<std::endl;
        }
    
        template< typename Tag, typename std::enable_if<!std::is_same<Tag, tag>::value, int>::type = 0>
        void f( T ){
            std::cout<<"non tag type" <<std::endl;
        }
     };
    

    【讨论】:

      【解决方案4】:

      我知道这可能不会让您满意,但我不相信您可能没有将专业化包含在非明确专业化的结构中。

      template<>
      template<>
      inline void C< tag1 >::foo< tag2 >( t_type ) {}
      

      【讨论】:

        【解决方案5】:

        基本细节是您需要将代码声明放在类之外,以便只有一个声明。如果你把它放在一个头文件中,声明给所有包括 c++ 源文件的文件,你最终会定义同一个类的多个实例。只需将模板化函数的声明放在头文件中,然后将该模板化函数的声明特化移动到您的 C++ 源文件中,一切都会好起来的,因为编译器会根据您使用的特化类型生成正确的引用你的源代码。

        例如,您想创建一个可扩展的 Number 类,例如 java 的 Number 类,以便您可以传递数值。如果它在 .h/.hpp 文件中,编译器将知道如何生成对每个特化的引用,因为返回类型是编译器为其生成引用的生成函数名称的一部分。

        class Number {
            Int32 intVal;
            double d;
            float  f;
            Int64 longVal;
            std::string strVal;
        public:
            template<T>
            T getValue();
            ... other functions needed go here...
        };
        

        在您的 C++ 源文件中,您只需编写以下内容。

        template<>
        Int32 Number::getValue() { return intVal; }
        template<>
        double Number::getValue() { return d; }
        template<>
        float Number::getValue() { return f; }
        template<>
        Int64 Number::getValue() { return longVal; }
        template<>
        std::string Number::getValue() { return strVal; }
        

        现在,当您传递一个数字时,根据您分配给它的值类型,您可以在 getValue() 调用中使用适当的值类型。

        【讨论】:

          【解决方案6】:

          虽然代码可能不合规,但一种实用的解决方案是切换到可以正常工作的 clang。

          https://godbolt.org/z/hPbP1M

          【讨论】:

            【解决方案7】:

            gcc 不允许类中的成员函数完全特化。 这是因为在功能方面它与函数重载相同。 因此,只需从特化中删除 template 并使其成为函数重载。

            【讨论】:

              【解决方案8】:

              试试这个:

              template <> template<typename T> inline void C<T> :: foo<tag2>(T) {}
              

              【讨论】:

              • 对不起 - 我想我混淆了两个模板限定符的顺序。尝试将带有空括号的那个放在最左边。
              • 再次不行(错误:不允许模板部分特化)'foo
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-06-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-11-27
              • 2021-12-28
              • 1970-01-01
              相关资源
              最近更新 更多