2016-09-10 67 views
3

假设我有一个基类foo和两个派生类AB。然后,我有另一个类bar,它有一个数据成员xyz,其可以是A,或,但类型的依赖于其他数据成员x_typey_type,和z_type,这些值不可用在编译时。我虽然关于使用模板数据成员和在构造函数中定义类型,我得到的类型的值,但显然至少在C++ 11中是不可能的。那么如何继续?基于另一个数据成员的类数据成员类型?

class foo{ 
public: 
    foo(double); 
    int x_type; 
    virtual double do_something(double, int) = 0; 
}; 

class A: public foo { 
public: 
    A(double, double); 
    double do_something(double, int); 

private: 
    double z1; 
    double z2; 
}; 

class B: public foo { 
public: 
    B(double); 
    double do_something(double, int); 

private: 
    double w; 
}; 

class bar { 
public: 
    bar(); 
    double do_something2(int); 

private: 
    int x_type; 
    int y_type; 
    int x_type; 
    x; // these are either A or B... 
    y; 
    z; 
}; 

而且在构造函数中我会像

if(x_type == 1){ 
    x = A(arg1, arg2); 
} else { 
    x = B(arg3); 
} 

在我的实际应用中可以有更高的许多派生类和未知类型的数据成员。我想知道是否有可能使bar具有多个模板参数的模板类,但我不确定这是可能的,因为参数类型取决于另一个参数?

+0

通过整数值枚举类型是Evil™反模式。可能这对应于带有指向多态类“foo”的设计。其中应该有一个虚拟析构函数。 –

回答

2

您需要使用多态,并采取共同的基类美孚的优势:

if(x_type == 1){ 
    x.reset(new A(arg1, arg2)); 
} else { 
    x.reset(new B(arg3)); 
} 

这是一个很好:

private: 
    int x_type; 
    int y_type; 
    int x_type; 
    std::unique_ptr<Foo> x; // these are either A or B... 
    std::unique_ptr<Foo> y; 
    std::unique_ptr<Foo> z; 

}; 

然后在你的构造函数,你可以从正确的类型创建XYZ练习将创建正确的Foo实例的代码移动到所谓的“工厂”类或函数中,以隐藏决策制定逻辑和构造细节(有时可能相当复杂)。

1

所有变量的静态类型必须在编译时已知,因此它不能根据运行时对象的值进行更改。使这项工作的方式是让xy,并且z都有型std::uniqe_ptr<foo>,然后动态地分配在运行时AB对象:

class bar { 
public: 
    bar(some_type something) { 
     if (something == some_value) { 
      b.x = new A(3.14, 12.34); 
     } else { 
      b.x = new B(456.78); 
     } 
    } 
private: 
    int x_type; 
    std::unique_ptr<foo> x; 
    //... 
}; 

int main() { 
    bar b(whatever()); 
} 

在这种情况下,你也应该申报foo::~foo()是虚拟的,以便确保派生的对象被正确销毁。

这也可能是一个好主意™,它可以完全消除x_type和朋友,并编写代码,在创建后不会关心x的实际类型。

1

我想知道是否有可能使栏模板类与多个模板参数,但我不知道这是可能的,因为参数类型取决于另一个参数吗?

我不知道这是否有帮助,但我会让它在这里以防万一。

您会发现,模板的不同特化可以从不同的类继承。所以,你可以有:

// fwd decl 
template <int kind> class bar; 

template <> class bar<1> : public A { 
public: 
    bar(double x, double y) : A(x,y) { } 
}; 

template <> class bar<2> : public B { 
public: 
    bar(double a) : B(a) { } 
}; 

在后一阶段,当你来到一个class C : public foo,你只需指定另一个kind到一个新的bar模板专门有你有它:使用bar作为统一(警告......但一个统一的类型 - 不低于普通foo祖先bar<1>bar<2>将是两种不同类型除外)

所以,好吧,如果你不想继承,你可以拥有它DIF不同has-a特定bar模板专业化。

template <int kind> class bar; 

template <> class bar<1> { 
    A val; 
public: 
    bar(double x, double y) : val(x,y) { } 

    void doSomething2(...) { 
    // use the val of type A 
    } 
}; 
template <> class bar<2> { 
    B val; 
    double y_; 
public: 
    bar(double x, double y) : val(x), y_(y) { } 

    void doSomething2(...) { 
    // use the val of type B and a separate y_ 
    } 
}; 
1

我虽然有关使用模板数据成员和 构造,在那里我得到的值类型,但显然这 是不可能的,至少用C定义类型++ 11

C++ 11通过使用make_*模板函数创建适当类型的附带处理取决于一些参数模板结构的标准架构目的。见例如make_tuple功能:

template <class T> 
struct foo { 
    T t; 
    foo(T t): t(t) { } 
}; 

template <class T> 
foo<T> make_foo(T t) { return foo<T>(t); } 
2

如果能够用于xyz所有类型都从派生的所有

auto t = std::make_tuple(1, "abc", 1.0); 
// decltype(t) == std::tuple<int, char const*, double> 

这可以通过创建模板类和建筑模板功能来实现共同基类,基指针解决方案,std::unique_ptr(Lyubomir Stankov +1)是(恕我直言)一个很好的解决方案。

但你问“是否有可能使栏具有多个模板参数的模板类”。

是的:这是可能的。不是很优雅(恕我直言),但可能。

我建议为了好玩以下解决方案,但我认为,在更一般的情况下(注意,在我的例子,AB是不相关的类,从foo没有更多的派生),可能是有用的(我希望如此)

#include <tuple> 
#include <string> 
#include <utility> 

class A 
{ 
    private: 
     double  d; 
     std::string s; 

    public: 
     A (double d0, std::string s0) : d { d0 }, s { s0 } { } 
}; 

class B 
{ 
    private: 
     long l; 

    public: 
     B (long l0) : l { l0 } { } 
}; 

template <typename Tx, typename Ty, typename Tz> 
class bar 
{ 
    private: 

     template <typename ... Ts> 
     using tpl = std::tuple<Ts...>; 

     template <std::size_t ... Is> 
     using is = std::index_sequence<Is...> const; 

     template <std::size_t N> 
     using mis = std::make_index_sequence<N>; 

     Tx x; 
     Ty y; 
     Tz z; 

     template <typename ... Tsx, std::size_t ... Isx, 
       typename ... Tsy, std::size_t ... Isy, 
       typename ... Tsz, std::size_t ... Isz> 
     bar (tpl<Tsx...> const & tx0, is<Isx...> const &, 
      tpl<Tsy...> const & ty0, is<Isy...> const &, 
      tpl<Tsz...> const & tz0, is<Isz...> const &) 
     : x { std::get<Isx>(tx0) ... }, 
      y { std::get<Isy>(ty0) ... }, 
      z { std::get<Isz>(tz0) ... } 
     { } 

    public: 

     template <typename ... Tsx, typename ... Tsy, typename ... Tsz> 
     bar (tpl<Tsx...> const & tx0, 
      tpl<Tsy...> const & ty0, 
      tpl<Tsz...> const & tz0) 
     : bar(tx0, mis<sizeof...(Tsx)> {}, 
       ty0, mis<sizeof...(Tsy)> {}, 
       tz0, mis<sizeof...(Tsz)> {}) 
     { } 
}; 

int main() 
{ 
    bar<A, B, A> aba{ std::make_tuple(2.3, "str1"), 
         std::make_tuple(4), 
         std::make_tuple(5.4, "str2") }; 

    bar<B, A, B> bab{ std::make_tuple(3), 
         std::make_tuple(3.2, "str3"), 
         std::make_tuple(5) }; 
} 

不幸的是该实施例中使用std::make_index_sequencestd::index_sequence和是C++ 14层的功能。

如果你想实现C++ 11 foo,则可以实现以下结构struct indexSeqstruct indexSeqHelper,替代std::index_sequencestd::make_index_sequence

template <std::size_t ...> 
struct indexSeq 
{ }; 

template <std::size_t N, std::size_t ... Next> 
struct indexSeqHelper 
{ using type = typename indexSeqHelper<N-1U, N-1U, Next ... >::type; }; 

template <std::size_t ... Next > 
struct indexSeqHelper<0U, Next ... > 
{ using type = indexSeq<Next ... >; }; 

,并定义ismis如下

template <std::size_t ... Is> 
    using is = indexSeq<Is...>; 

    template <std::size_t N> 
    using mis = typename indexSeqHelper<N>::type; 
相关问题