【问题标题】:Class method specialisation with different signature具有不同签名的类方法特化
【发布时间】:2012-12-11 12:11:59
【问题描述】:

我正在尝试编写一个类模板,其中方法签名根据模板参数而变化。我的目标是尽可能少地重复代码。考虑这个例子,首先是类声明:

// a.hxx
#ifndef A_HXX
#define A_HXX

template<typename T>
struct A
{
    void foo(T value) const;
    void bar() const;
};

#include <string>

#ifndef short_declaration
template<>
struct A<std::string>
{
    void foo(const std::string &value) const;
    void bar() const;
};
#else // short_declaration
template<>
struct A<std::string>
{
    void foo(const std::string &value) const;
};
#endif // short_declaration

#endif // A_HXX

现在是类定义:

// a_impl.hxx
#ifndef A_IMPL_HXX
#define A_IMPL_HXX

#include "a.hxx"
#include <iostream>
#include <typeinfo>

template<typename T>
void A<T>::foo(T value) const
{
    std::cout << "A<T=" << typeid(T).name() << ">::foo(" << value << ")"
        << std::endl;
}

template<typename T>
void A<T>::bar() const
{
    std::cout << "A<T=" << typeid(T).name() << ">::bar()" << std::endl;
}

void A<std::string>::foo(const std::string &value) const
{
    std::cout << "A<std::string>::foo(" << value << ")" << std::endl;
}

#ifndef skip_duplicates
void A<std::string>::bar() const
{
    std::cout << "A<std::string>::bar()" << std::endl;
}
#endif // skip_duplicates

#endif // A_IMPL_HXX

现在是一个测试程序:

// test.cxx

//#define skip_duplicates
//#define short_declaration
#include "a_impl.hxx"

int main(void)
{
    A<int>         obj1;
    A<std::string> obj2;
    int         value1(1);
    std::string value2("baz");

    obj1.foo(value1);
    obj1.bar();

    obj2.foo(value2);
    obj2.bar();

    return 0;
}

如果这样编译,我会得到(对于我的 typeid 实现)的预期输出:

A<T=i>::foo(1)
A<T=i>::bar()
A<std::string>::foo(baz)
A<std::string>::bar()

但在我的示例中,我当然想要一种启用skip_duplicates 甚至short_declaration 的方法。对于有点similar questionecatmur 回答说需要给出完整的类,所以至少定义short_declaration 是行不通的。

其他人如何处理使用可能将大对象作为参数的方法创建类模板的问题?

【问题讨论】:

    标签: c++ templates template-specialization


    【解决方案1】:

    您可以将重复项提取到基类中:

    template<typename T>
    struct Base{
        void Bar()
        {
            std::cout << "A<T=" << typeid(T).name() << ">::bar()" << std::endl;
        }
    
    protected:
        ~Base(){}
        template<typename U>
        void DoFoo(U value)
        {
            std::cout << "A<T=" << typeid(T).name() << ">::foo(" << value << ")"
            << std::endl;
        }
    };
    
    template<typename T>
    struct A : Base<T> {
        void Foo(T value)
        {
            DoFoo(value);
        }
    };
    
    template<>
    struct A<std::string> : Base<std::string> {
        void Foo(const std::string& value)
        {
            DoFoo(value);
        }
    };
    

    【讨论】:

    • 您甚至可以写成template&lt;typename T, typename U=T&gt; struct NarrowBase:Base&lt;T&gt;{void Foo(const U&amp; value){DoFoo(value);}};,这会将A&lt;std::string&gt; 缩减为template&lt;&gt; struct A&lt;std::string&gt;:NarrowBase&lt;std::string&gt; {};,从而更加紧凑。
    • @hansmaad:在基类中分离出非专业方法是一个非常好的建议。但是将基类中的专用方法作为受保护的方法模板对我不起作用:value 的指针在Base::DoFoo()A::Foo() 中是不同的。
    • @handmaad, @Yakk:Yakks 建议的变体,Base 类模板本身已经接受了两个参数 TU,并且类模板定义 A 设置了 @ 987654332@ 然后特化显式设置U=const std::string &amp;
    【解决方案2】:

    根据hansmaad 的回答和Yakk 的评论,我认为以下是我将采用的解决方案:

    // a.hxx
    #ifndef A_HXX
    #define A_HXX
    
    template<typename T, typename U=T>
    struct Abase
    {
        void foo(U value) const;
        void bar() const;
    };
    
    template<typename T>
    struct A : Abase<T> { };
    
    #include <string>
    
    template<>
    struct A<std::string> : Abase<std::string, const std::string &> { };
    
    #endif // A_HXX
    

    还有这个实现:

    // a_impl.hxx
    #ifndef A_IMPL_HXX
    #define A_IMPL_HXX
    
    #include "a.hxx"
    #include <iostream>
    #include <typeinfo>
    
    template<typename T, typename U>
    void Abase<T, U>::foo(U value) const
    {
        std::cout << "A<T=" << typeid(T).name() << ",U=" << typeid(U).name()
            << ">::foo(" << value << "): &value=" << int(&value) << std::endl;
    }
    
    template<typename T, typename U>
    void Abase<T, U>::bar() const
    {
        std::cout << "A<T=" << typeid(T).name() << ",U=" << typeid(U).name()
            << ">::bar()" << std::endl;
    }
    
    #endif // A_IMPL_HXX
    

    测试程序可以保持原样,也可以增加如下几行:

    //...
        std::cout << "&value=" << int(&value1) << std::endl;
    //...
        std::cout << "&value=" << int(&value2) << std::endl;
    //...
    

    感谢您的回答和建议!

    【讨论】:

      最近更新 更多