基于另一个数据成员的类数据成员类型
Class data member type based another data member?
假设我有一个基类foo
和两个派生类A
和B
。然后我有另一个类bar
,它有一个数据成员x
, y
, z
,它可以是A
,或者,但是类型取决于其他数据成员x_type
, y_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
具有多个模板参数的模板类,但我不确定是否有可能,因为参数类型取决于另一个参数?
您需要使用多态性并利用公共基类Foo:
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;
};
在你的构造函数中,你可以用正确的类型创建x y z:
if(x_type == 1){
x.reset(new A(arg1, arg2));
} else {
x.reset(new B(arg3));
}
将创建正确Foo实例的代码移到所谓的"工厂"类或函数中,以隐藏决策逻辑和构造细节(有时可能相当复杂),这是一个很好的实践。
如果所有可用于x
, y
和z
的类型都是所有派生自一个公共基类,则基指针解决方案,std::unique_ptr
(Lyubomir Stankov +1),是(IMHO)一个很好的解决方案。
但是你问"是否有可能使bar成为一个有多个模板参数的模板类"。
是的,这是可能的。不是很优雅(恕我直言),但可能。
我提出以下解决方案的乐趣,但我认为,在更一般的情况下(请注意,在我的例子中,A
和B
是不相关的类,而不是更多的衍生自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_sequence
和std::index_sequence
,它们是c++ 14的特性。
如果你想在c++ 11中实现foo
,你可以实现下面的结构体struct indexSeq
和struct indexSeqHelper
来代替std::index_sequence
和std::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 ... >; };
并定义is
和mis
,如下所示
template <std::size_t ... Is>
using is = indexSeq<Is...>;
template <std::size_t N>
using mis = typename indexSeqHelper<N>::type;
所有变量的静态类型必须在编译时已知,因此它不能根据运行时对象的值进行更改。实现此工作的方法是使x
, y
和z
都具有std::uniqe_ptr<foo>
类型,然后在运行时动态分配A
或B
对象:
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()
为virtual,以便确保派生对象被正确地销毁。
完全消除x_type
和它的朋友,并在创建x
后编写不关心其实际类型的代码,这也是一个普遍的好主意。
我想知道是否有可能使bar具有多个模板参数的模板类,但我不确定是否有可能作为参数类型取决于另一个参数?
我不知道这是否有帮助,但我把它放在这里,以防万一。
你看,一个模板的不同专门化可以从不同的类继承。所以你可以输入:
// 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>
将是两个不同的类型)
那么,好吧,如果你不想要继承,你可以在特定的bar
模板特化中通过不同的has-a
来实现它。
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_
}
};
c++ 11提供了标准模式,通过使用我考虑使用模板数据成员并定义类型构造函数,在那里我得到类型的值,但显然至少在c++ 11
中是不可能的
make_*
模板函数创建适当类型的对象来处理依赖于某些参数的模板化构造。参见make_tuple
函数:
auto t = std::make_tuple(1, "abc", 1.0);
// decltype(t) == std::tuple<int, char const*, double>
这可以通过创建模板类和模板构造函数来实现:
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); }
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 在类 A 中创建类型为 B 类的向量 - 访问数据 [C++] [成员在两个类中都是私有的]
- 是否可以根据其数据成员的类型确定类型的大小
- 拒绝包含某些公共静态数据成员的类型
- 复制构造抽象类型的数据成员
- 引用类型的数据成员提供有关恒常正确性"loophole"
- 如果类在 C++ 中具有常量或引用类型的非静态数据成员,为什么编译器不提供默认赋值运算符?
- 具有引用数据成员的结构不是文本类型吗?
- 类数据成员指针的非类型模板参数包无法使用 gcc 编译
- 标记为 [数据成员] 时的意外类型'USN'
- 依赖于依赖类型的非静态数据成员的非限定名称
- 基类数据成员类型取决于派生类
- 类型为专用数据成员的静态成员
- QString:包括非文本类型的静态数据成员的初始化
- 由于标准而指向 Base 数据成员的指针类型
- sizeof参考对于类型和数据成员的不同
- 参考类型静态数据成员的问题可能是编译器错误
- 指向不完整类型的数据成员
- 联合类型的数据成员的初始化
- 基于另一个数据成员的类数据成员类型