由于未使用的变量,请忽略警告行,算法是虚拟示例函数。
另外,对于冗长的帖子感到抱歉,我试图尽可能地缩短它。
给出了以下标记类型和标记结构:
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
,则期望Container<OrdinaryType, Allocator<OrdinaryType>>
将OrdinaryType
ordinary_tag
提供enable_if
参数 - 这是is_same
的布尔参数,应由first_type
提供。我尝试使用以下列方式将 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);
标记为普通的STL容器:
first_type_list/vector
我没有将ordinary_collection_tag
识别为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’
- ed类型,而是收到以下错误:
tag
现在,当我没有根据OrdinaryType
的标记启用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
标有{{1}}。这是我的预期。
那么,我做错了什么?
我正在使用gcc 4.8.2。
可以找到完整的小程序here。
答案 0 :(得分:1)
由于还没有人回答,我找到了问题的可能解决方案,我决定发布它。
我没有像我在问题中那样尝试对tag
部分专门化任何容器,而是假设拥有一个元素容器是一般情况。因此,tag<Type>
模板将Type
视为集合。如果Type
不满足此条件,则模板推导选择适合的Type
的另一个专门化:单个元素的专门化。通过引入collection
结构来强加该条件。任何可用value_type
的容器现在都被识别为标记元素的集合。
这是解决方案(我只是将类型的名称更改为水果名称,我想让它更容易阅读):
#include <type_traits>
#include <iostream>
#include <list>
#include <vector>
#include <map>
namespace tags {
struct apple_tag {};
struct banana_tag {};
struct apple_collection_tag {};
struct banana_collection_tag {};
template<typename Tag>
struct collection {};
template<>
struct collection<apple_tag>
{
typedef apple_collection_tag type;
};
template<>
struct collection<banana_tag>
{
typedef banana_collection_tag type;
};
template<typename Type>
struct tag
{
typedef typename collection<typename tag<typename Type::value_type>::type>::type type;
};
// Select tags of pairs based on the second type. Used for maps (key, value) pairs.
template
<
typename First,
typename Second
>
struct tag<std::pair<First, Second>>
{
typedef typename tag<Second>::type type;
};
}
struct apple {};
namespace tags {
template<>
struct tag<apple>
{
typedef apple_tag type;
};
}
struct banana {};
namespace tags {
template<>
struct tag<banana>
{
typedef banana_tag type;
};
}
template<typename Type>
struct my_container
{
typedef Type value_type;
};
namespace implementation {
template<typename Type, typename Tag>
struct function {};
template<typename Type>
struct function<Type, tags::apple_tag>
{
static void apply(Type const& t)
{
std::cout << "apple" << std::endl;
}
};
template<typename Type>
struct function<Type, tags::banana_tag>
{
static void apply(Type const& t)
{
std::cout << "banana" << std::endl;
}
};
template<typename Type>
struct function<Type, tags::apple_collection_tag>
{
static void apply(Type const& t)
{
std::cout << "apple collection" << std::endl;
}
};
template<typename Type>
struct function<Type, tags::banana_collection_tag>
{
static void apply(Type const& t)
{
std::cout << "banana collection" << std::endl;
}
};
}
// Value tag Dispatcher
template<typename Type>
void function(Type const & t)
{
implementation::function<Type, typename tags::tag<Type>::type>::apply(t);
}
int main(int argc, const char *argv[])
{
typedef std::list<apple> apple_bag;
apple_bag abag;
function(abag);
typedef std::vector<apple> apple_box;
apple_box abox;
function(abox);
typedef std::map<int, apple> apple_orchard;
apple_orchard ao;
function (ao);
// my_container has value_type, so it can be used as well.
typedef my_container<banana> banana_bag;
banana_bag bo;
function(bo);
return 0;
}
就是这样。这是输出:
apple collection
apple collection
apple collection
banana collection