2

我有一个Foo<T>我派生的模板类,FooDerived<T>它又是模板类的模板参数Bar<T>,即我最终得到类似Bar< FooDerived<T> >.

template <typename T>
class Foo
{
    public:
        typedef T type_t;

        virtual double compute() { return 1.0; }
};

template <typename T>
class FooDerived : public Foo<T>
{
    public:
        double compute() { return 42.0; }
};

template <typename T>
class Bar : public T
{
    public:
        void f()
        {
            // This function could vary depending on U (and possibly T)
            std::cout << "Bar< T<U> > : " << this->compute() << std::endl;
        }
};

我正在尝试Bar基于FooDerived. 例如,Bar< FooDerived<int> >并且Bar< FooDerived<float> >会有不同的行为。

int main(void)
{
    Bar< FooDerived<int> > a;
    a.f();
    Bar< FooDerived<float> > b;
    b.f();
}

如何在 C++03(或 C++11)中有效地实现这一点?高效,我的意思是我想避免无用的代码重复(实际程序涉及的类型和函数比这个例子多得多)。此外,使用代码的人应该能够在不需要修改代码的情况下添加他们的专业化,因此任何类型的switch解决方案都不会被采用。

我一直在寻找类似 SFINAE 的解决方案,boost::mpl例如检查类型,但似乎没有什么能实现我的目标。我想这可能不适用于那种模板专业化。因为编译器似乎并不认为它是一种专业化,所以我总是以这种方式结束,例如,如果我尝试做这样的事情:boost::mpl::if_boost::is_sameerror: redefinition of 'Bar'

模板类 Bar : public T, private boost::mpl::if_, int, boost::mpl::false_>

使用boost::mpl::if_,无论是作为私有继承还是额外的模板参数,似乎都无法实现特化。

那么实现这样的事情的正确方法是什么?

更新 1

专门化所有案例是可能的,但隐藏在这个例子背后的真正问题实际上要复杂得多。我有一个CachedFunction<T> : public TwhereT派生自Function<U>(函数、可微函数、两次可微函数等) whereU是存储类型(密集或稀疏矩阵)。CachedFunction包含大量函数,其实现取决于存储类型和函数类型。因此,元编程似乎是避免某些代码重复的好方法。

更新 2

作为对所提供答案的回应:对于所有涉及的案例,我试图避免这些显式的模板专业化。试着想象有 3 或 4 个派生自 的类Foo,有 2 或 3 种类型Foo,并且Bar包含 6 或 7 个函数,需要根据所Foo考虑的派生类的类型和派生类进行不同的处理。基本上,对于每个i,jk,我需要实现:

template<> void Bar<FooDerived_i<Type_j> >::f_k(){ ... }

因此,我试图看看是否有任何其他“更清洁”的方式。

更新 3

如果我使用boost::is_same,我可以做这样的事情,但是这个解决方案使得在不修改代码的情况下处理新类型变得更加困难。

这是一个例子

#include <iostream>
#include <boost/type_traits/is_same.hpp>

typedef int type1;
typedef float type2;

template <typename T>
class Foo
{
    public:
        typedef T type_t;

        virtual double compute() { return 1.0; }
};

template <typename T>
class FooDerived
{
    public:
        typedef T type_t;

        double compute() { return 42.0; }
};

template <class T>
class Bar : public T
{
public:
    void f()
    {
        // types have to be known...
        if (boost::is_same<typename T::type_t, type1>::value)
            std::cout << "Bar< T<type1> > : " << this->compute() << std::endl;
        else if (boost::is_same<typename T::type_t, type2>::value)
            std::cout << "Bar< T<type2> > : " << this->compute() << std::endl;
    }
};

int main(void)
{
    Bar< Foo<type1> > a;
    a.f();
    Bar< FooDerived<type2> > b;
    b.f();
}
4

3 回答 3

0

(注意,这回答了问题的第一个版本 如果您认为应该删除它,请通过评论告诉我)

你可以做:

template <typename T>
class Bar : public T
{
    public:
        std::string name();
};

template<>
std::string Bar<FooDerived<int> >::name() {
  return std::string("int");
  }

template<>
std::string Bar<FooDerived<float> >::name() {
  return std::string("float");
  }

在这里测试:http ://coliru.stacked-crooked.com/view?id=9054d3356f438b31b1adbb595620d838-ad7854d9cfd7979d567ca413f0830b65

感谢 jogojapan 的宝贵建议!

于 2013-06-28T06:52:43.840 回答
0

这个怎么样?

template<>
struct Bar< FooDerived<int> >
{
   // int spezialisation here
};

template<>
struct Bar< FooDerived<float> >
{
   // float spezialisation here
};

对于只专门化一些具有不同行为但没有不同签名的函数,有以下方法:

template<>
std::string Bar<FooDerived<float> >::name() {
  return std::string("float");
}

更新:

作为对您第二次更新的说明,我认为专业化是在这种情况下实现不同行为的唯一干净方法。但是,如果您以更精细的方式使用模板,您当然可以重用大量代码:

template<class T>
std::string name_impl(T& t) {
  // generic implementationm
}

std::string name_impl(Bar<FooDerived<float>& t) {
  // implementation
}

template<class T>
std::string Bar<T>::name() {
  // something befor
  return name_impl(*this);
}

因此,您可以通过函数重载切换到编译时多态性(使用继承,enable_if,...),这可能比模板专业化更容易。这是编译时的模板模式

编辑: 作为您的第三次更新,我建议您在这里阅读我的答案。通过您的实现,您很可能会在方法中遇到问题f

于 2013-06-28T06:55:42.050 回答
0

正如您在一条评论中所说,在现实世界的示例中,专业化该类将涉及大量代码重复,因此应避免。

如果你想,正如你在问题中所说,“专门化”关于 Foo (或 FooDerived,就此而言)的模板参数,你可能应该考虑在 Bar 中定义一个模板模板参数,否则你肯定可以实例化一个例如 Bar,因此 Bar 的模板参数没有模板参数。

您可以通过

template <template<typename> class T, typename Arg> class Bar : public T<Arg> {
public:
    std::string name()
    {
        return "Bar<T<Arg>>";
    }
};

也就是说,下面的代码展示了一种无需专门化整个类即可获得专门化行为的方法

  1 #include <string>
  2 #include <iostream>
  3 
  4 namespace ExampleSpecializations{
  5         template <typename T>
  6         std::string name(){
  7                 return "generic";
  8         }       
  9         template <>
 10         std::string name<int>(){
 11                 return "int";
 12         }
 13 }
 14 
 15 template <typename Arg>
 16 struct Bar {
 17         std::string name()
 18         {
 19                 std::string ret = std::string("Bar<") + ExampleSpecializations::name<Arg>() + std::string(">");
 20                 return ret; 
 21         }       
 22 };      
 23 
 24 int main()
 25 {
 26         Bar<int> a;
 27         std::cout << a.name() << std::endl;
 28         Bar<char> b; 
 29         std::cout << b.name() << std::endl;
 30 }       
 31 

请注意,使用外部函数(从第 4 行到第 13 行)也提供了良好的封装,因为您没有添加不需要访问相关类内部任何内容的成员函数。

  1 #include <string>
  2 #include <iostream>
  3 
  4 template <typename T>
  5 struct Foo1{
  6 };              
  7                         
  8 template <typename T>   
  9 struct Foo2{
 10 };              
 11 
 12 namespace ExampleSpecializations{
 13         template <typename T>
 14                 std::string name(const T& ){
 15                         return "generic"; 
 16                 }       
 17         template <>
 18                 std::string name(const int&){
 19                         return "int";
 20                 }       
 21         template <typename T>
 22                 std::string name(const Foo1<T>& ){
 23                         std::string  ret = "Foo1<" + ExampleSpecializations::name<T>(T()) + ">";
 24                         return ret;  
 25                 }       
 26         template <typename T>
 27                 std::string name(const Foo2<T>& ){
 28                         std::string  ret = "Foo2<" + ExampleSpecializations::name<T>(T()) + ">";
 29                         return ret;  
 30                 }       
 31 }               
 32 
 33 template <typename Arg>
 34 struct Bar {
 35         std::string name()
 36         {
 37                 std::string ret = std::string("Bar<") + ExampleSpecializations::name<>(Arg()) + std::string(">");
 38                 return ret; 
 39         }       
 40 };      
 41 
 42 int main()
 43 {
 44         Bar<int> a;
 45         std::cout << a.name() << std::endl;
 46         Bar<char> b; 
 47         std::cout << b.name() << std::endl;
 48         
 49         Foo1<int> f1i;
 50         std::cout << ExampleSpecializations::name(f1i) << std::endl;
 51         
 52         Foo2<int> f2i;
 53         std::cout << ExampleSpecializations::name(f2i) << std::endl;
 54         
 55         Bar<Foo2<int> > bf2i;
 56         std::cout << bf2i.name() << std::endl;
 57 }

试试这里的代码 不用说,我建议也将 Bar::name 设为非成员函数。

最后,如果您不想提供“通用”行为,则根本不提供默认实现,如下所示:

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

通过这种方式,前面的代码会发出错误,因为在 Bar 上调用了“name”方法。该错误通过注释掉第 47 行或实现 char 特化来解决。

于 2013-06-28T07:15:34.710 回答