C++ 确保无变体的类型安全
考虑以下类别:C++ 确保无变体的类型安全,c++,templates,C++,Templates,考虑以下类别: template <typename T> class A { public: A(B<T> b) : b_(b) { } T foo() { return b_.foo(); } private: class B<T> b_; } template typename<T> class B { public: T foo(); } 模板 甲级{ 公众: A(B):B_uB(B){} T foo
template <typename T>
class A {
public:
A(B<T> b) : b_(b) { }
T foo() {
return b_.foo();
}
private:
class B<T> b_;
}
template typename<T>
class B {
public:
T foo();
}
模板
甲级{
公众:
A(B):B_uB(B){}
T foo(){
返回b_.foo();
}
私人:
B类;
}
模板类型名
B类{
公众:
T foo();
}
这很好地加强了跨堆栈的键入(您可以不断添加更多层并在单个类型上键入它们。但是,我希望在第二层上有两个不同的选项:
template <typename T, typename Y>
class A {
public:
T foo() {
return b_.foo();
}
Y foo() {
return c_.foo();
}
private:
class B<T> b;
class C<Y> c;
}
template typename<T>
class B {
public:
T foo();
}
template typename<T>
class C {
public:
T foo();
}
模板
甲级{
公众:
T foo(){
返回b_.foo();
}
余福义(){
返回c_.foo();
}
私人:
乙级;
丙级;;
}
模板类型名
B类{
公众:
T foo();
}
模板类型名
C类{
公众:
T foo();
}
有什么方法可以用多个类型名模板化类并实现这些方案吗?请注意,在某些情况下,T
和Y
可能是相同的,因此必须有更多的区别(例如B
vsC
)
我相信我可以有多个函数foo1和foo2返回不同的值。但是,我正在寻找可扩展的解决方案,客户可以提供他们自己的类型名,可能不止两个。理想情况下,会有一个重载(可能获取内部类的标识?)你的想法很难理解,但我想你的意思是获取一个
模板typename
列表和相等数量的typename
并构建其压缩应用程序的产品
// wrap a template<typename> typename into a normal type
// e.g. index_of_v<std::vector, std::vector> fails, but
// index_of_v<suspend<std::vector>, suspend<std::vector>> gives 0
template<template<typename> typename>
struct suspend { };
// search for X in Xs and produce a static constexpr std::size_t member values containing the index
// This is just the natural functional programming way to search a linked list
// indexOf(x, Nil) = undefined
// indexOf(x, Cons(y, xs)) = x == y ? 0 : 1 + indexOf(x, xs)
// deriving from std::integral_constant is really just noise;
// could be replaced with an explicit member definition
// but it's considered idiomatic to do this for some reason that I've forgotten
template<typename X, typename... Xs>
struct index_of { }; // base case, the default template only fires when Xs is empty, so the index is undefined
template<typename X, typename... Xs>
struct index_of<X, X, Xs...> : std::integral_constant<std::size_t, 0> { }; // if X is at the top of the list, it has index 0
template<typename X, typename Y, typename... Xs>
struct index_of<X, Y, Xs...> : std::integral_constant<std::size_t, index_of<X, Xs...>::value + 1> { }; // if it isn't, find it's index relative to the tail of the list, then shift it to be the index relative to the whole
// instead of writing index_of<X, Xs..>::value, write index_of_v<X, Xs...>
// this is a convention that you see in the standard library
template<typename X, typename... Xs>
inline constexpr std::size_t index_of_v = index_of<X, Xs...>::value;
// a class cannot have two lists of variadic template parameters
// the easiest thing to do is split the templating into two stages
// the outer template, choices, takes a list of templates as parameters
// template<typename T> class std::vector;
// int is a "base" type, so you can pass int to std::vector<int>
// but std::vector is not like int: std::vector<std::vector> is meaningless
// std::vector alone is just a template<typename> typename
// a function from types to types
template<template<typename> typename... Cs>
struct choices {
// 2nd "stage" takes the list of "normal" types
template<typename... Ts>
class type {
// apply each template to the corresponding base type
// the resulting list of base types is passed to std::tuple
// there's a "field" for each Cs<Ts> inside the tuple
std::tuple<Cs<Ts>...> parts;
public:
// hopefully self-explanatory
type(Cs<Ts>... parts) : parts(parts...) { }
// return the result of calling foo() on the container identified by C
template<template<typename> typename C>
auto foo() {
// we know the list of all the Cs,
// so just find the index of C in it and pass that to std::get
// then pass in parts to get the desired object, then call foo()
return std::get<index_of_v<suspend<C>, suspend<Cs>...>>(parts).foo();
}
};
// there's no luck for deducing Cs, since in order to *get* to types we need to specify Cs
// (choices::type is not a thing, you always write choices<Cs...>::type)
// But you can deduce `Ts`, by looking at the things contained in each container
// so, deduction guide
template<typename... Ts>
type(Cs<Ts>...) -> type<Ts...>;
};
如果返回类型不同,则多态性不适用于同名函数。但是,对于
t
=Y
的情况,您可能会考虑模板专门化或处理此问题。即使t=Y,那么t=a.foo();
会做什么?(如果t!=Y,您可以通过两次转换返回代理。)非常感谢您的评论。我正在考虑允许将类型标识传递到不同的多态函数中。这样行吗?@gruszczy:您的代码显然很混乱。在一个地方,您使用B
作为类型名:a(B条)
。但是在另一个例子中,你使用B
作为模板名称:B
不能同时是这两个,那么它是哪一个呢?还有,B
和模板条之间的关系是什么?为什么在不同的位置有许多虚假的类
呢?嘿@gruszczy,你的问题看起来真的不清楚。您能添加一些可能使用的简短示例吗?实际上,您可以使用std::enable_if
,使用相同名称但返回不同类型的函数。但是,如果没有您期望的示例,我无法告诉您确切的方法。这看起来是我需要的,但老实说,我在理解上有问题(我主要是问,因为我对模板不是很在行)。你会不会很友好,通过各行解释它们的用途?而且,这似乎是非常具体的(至少目前是这样),因为gcc不喜欢扣减指导(我想错了)。gcc是否有这样的功能?还有一条评论:这是一个惊人的壮举,但似乎需要很强的模板知识。如果我不再需要choices::type中的一个foo函数和无限数量的参数,它是否可以简化?我认为这不需要任何“强大的知识”唯一可能被认为是复杂的事情是index\u of
,但我想你会在图书馆里找到它。是的,如果你删除foo
,那么你就不需要index\u of
,这就是复杂性所在。但是,像这样删除foo
,似乎无法解决问题。但不完全确定如何删除为了让GCC不再是它自己。cppreference告诉我,演绎指南在一个类中应该可以正常工作。你可以尝试创建某种包装类,但在一个类问题上,你会遇到整个两个可变模板参数包,我认为你不能再为给定的容器集命名部分专门化。
template<typename T>
struct B {
T x;
B(T x) : x(x) { }
T foo() { return x; }
};
using A1 = choices<B>;
void demonstration1() {
A1::type a(B(5)); // note the deduction guide at work
std::cout << "B: " << a.foo<B>() << "\n";
}
template<typename T>
struct C {
T x;
C(T x) : x(x) { }
T foo() { return -x; } // keeping it interesting
};
using A2 = choices<B, C>;
void demonstration2() {
A2::type a(B('a'), C(5));
std::cout << "B: " << a.foo<B>() << "; C: " << a.foo<C>() << "\n";
}