【发布时间】:2014-03-21 16:56:32
【问题描述】:
由于未使用变量,请忽略警告行,算法是虚拟示例函数。
另外,很抱歉这篇文章太长了,我尽量缩短它。
给出以下标签类型和标签结构:
namespace tags {
struct ordinary_tag{};
struct special_tag {};
struct extra_special_tag {};
struct ordinary_collection_tag {};
struct special_collection_tag {};
template<typename Type>
struct tag
{
typedef void type;
};
}
以及用于算法参数的具体类:
class concrete_one {};
class concrete_two {};
implementation 命名空间存储算法algorithm 的实现,基于算法结果的类型,可以是任何类型,带有特定标签。结果的标签决定了选择的算法:
namespace implementation {
template<typename Result, typename Tag>
struct algorithm {};
template<typename Result>
struct algorithm<Result, tags::ordinary_tag>
{
static Result apply(concrete_one const & a1, concrete_two const & a2)
{
Result r;
std::cout << "ordinary" << std::endl;
// Modify r using a1, a2.
return r;
}
// Commutative algorithm.
static Result apply(concrete_two const & a1, concrete_one const & a2)
{
return apply(a2, a1);
}
};
template<typename Result>
struct algorithm<Result, tags::special_tag>
{
static Result apply(concrete_one const & a1, concrete_two const & a2)
{
Result r;
std::cout << "special" << std::endl;
// Modify r using a1, a2.
return r;
}
};
...
并且算法也被标记为标记元素类型的集合,例如当 Result 被标记为普通类型的集合时:
template<typename Result>
struct algorithm<Result, tags::ordinary_collection_tag>
{
static Result apply(concrete_one const & a1, concrete_two const & a2)
{
Result r;
std::cout << "ordinary collection" << std::endl;
// Modify r using a1, a2.
return r;
}
};
implementation 命名空间中的算法由使用可变参数的函数模板调度:
template<typename Result, typename ... Arguments>
Result algorithm(Arguments ... args)
{
// Dispatch to the appropriate algorithm based on the result tag
// and overload within the algorithm structure for the variadic arguments
return implementation::algorithm<Result, typename tags::tag<Result>::type>::apply(args ...);
}
某些类型的定义和标记不同:
struct first_type {};
namespace tags {
// Make first_type behave as ordinary type.
template<>
struct tag<first_type>
{
typedef ordinary_tag type;
};
}
struct second_type {};
namespace tags {
// Make second_type behave as a special type.
template<>
struct tag<second_type>
{
typedef special_tag type;
};
}
并且它们按预期工作得非常好:
concrete_one c1;
concrete_two c2;
first_type f1 = algorithm<first_type>(c1, c2);
second_type f2 = algorithm<second_type>(c1, c2);
但问题在于tag 的特殊化,以考虑任何具有分配器的容器,并根据容器元素类型的标记对其进行标记。这是我试图做的:
namespace tags
{
// An attempt to tag all Containers with Allocator of ordinary tagged types using ordinary_collection_tag.
template
<
typename OrdinaryType,
template <typename, typename> class Container,
template <typename> class Allocator
>
struct tag
<
typename std::enable_if
<
std::is_same<typename tags::tag<OrdinaryType>::type, tags::ordinary_tag>::value, // true if OrdinaryType is tagged with ordinary_tag
Container<OrdinaryType, Allocator<OrdinaryType>> // Use this as the T argument of enable_if
>::type // in enable_if specialized for "true" :: typename T type;
>
{
typedef ordinary_collection_tag type;
};
}
预计enable_if 将提供T 参数,如果人名OrdinaryType 确实被标记为ordinary_tag - 这是enable_if 的布尔参数,应该由is_same。我尝试通过以下方式使用将first_type 标记为普通的 STL 容器:
typedef std::list<first_type> first_type_list;
typedef std::vector<first_type> first_type_vector;
first_type_list fl = algorithm<first_type_list>(c1, c2);
first_type_vector fv = algorithm<first_type_vector>(c1, c2);
我没有将first_type_list/vector 识别为ordinary_collection_tag-ed 类型,而是收到以下错误:
test-other.cpp:158:12: error: template parameters not used in partial specialization:
struct tag
test-other.cpp:158:12: error: ‘OrdinaryType’
test-other.cpp:158:12: error: ‘template<class, class> class Container’
test-other.cpp:158:12: error: ‘template<class> class Allocator’
现在,当我不启用基于OrdinaryType 标记的tag 专业化时,我将它专门用于任何OrdinaryType,如下所示:
// Works but doesn't see that OrdinaryType should be tagged with ordinary_tag,
// std::list<first_type> and std::vector<second_type> are both tagged ordinary_collection_tag.
//namespace tags
//{
//template
//<
//typename OrdinaryType,
//template <typename, typename> class Container,
//template <typename> class Allocator
//>
//struct tag
//<
//Container<OrdinaryType, Allocator<OrdinaryType>>
//>
//{
//typedef ordinary_collection_tag type;
//};
//};
然后像std::vector<first_type> 和std::list<second_type> 这样的类型都被标记为ordinary_collection_tag,即使second_type 被标记为special_tag。这是我所期待的。
那么,我做错了什么?
我使用的是 gcc 4.8.2。
完整的小程序可以在here找到。
【问题讨论】:
-
编译器产生相当准确的诊断:
struct tag的这种“专业化”并没有真正使用它的模板参数。 -
@Constructor,是的,这就是我的想法,但问题仍然存在:我如何将
tag专门用于所有包含标记元素的容器?现在我正在尝试使用value_type来做到这一点,并依靠模板推导机制,但我仍然失败了..
标签: c++ templates typetraits