C++ 基于类型的标记分派:是否可以根据容器元素的标记对容器进行不同的标记?
请忽略警告行,因为未使用变量,算法是虚拟示例函数 另外,很抱歉我的帖子太长了,我试着尽量缩短它 给出了以下标记类型和标记结构:C++ 基于类型的标记分派:是否可以根据容器元素的标记对容器进行不同的标记?,c++,templates,typetraits,C++,Templates,Typetraits,请忽略警告行,因为未使用变量,算法是虚拟示例函数 另外,很抱歉我的帖子太长了,我试着尽量缩短它 给出了以下标记类型和标记结构: namespace tags { struct ordinary_tag{}; struct special_tag {}; struct extra_special_tag {}; struct ordinary_collection_tag {}; struct special_collection_tag {};
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;
};
}
implementation
命名空间根据算法结果的类型存储算法的实现,该类型可以是任何类型,并使用特定标记进行标记。结果的标记决定了所选的算法:
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;
}
};
...
某些类型的定义和标记不同:
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;
};
}
如果名为OrdinaryType
的人真的被标记为普通标签
,则enable_if
将作为T
参数提供容器
——这是应该提供的enable_if
的布尔参数。我尝试使用STL容器来保存标记为普通的first\u 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);
现在,当我不基于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;
//};
//};
//可以工作,但看不到OrdinaryType应该使用普通的_标记进行标记,
//std::list和std::vector都被标记为普通集合标记。
//名称空间标记
//{
//模板
//<
//typename OrdinaryType,
//模板类容器,
//模板类分配器
//>
//结构标签
//<
//容器
//>
//{
//typedef普通_集合_标签类型;
//};
//};
然后,像std::vector
和std::list
这样的类型都用普通集合标签
标记,即使第二个类型
用特殊标签
标记。这是我所期望的
那么,我做错了什么
我使用的是GCC4.8.2
完整的小程序可以找到 由于还没有人回答,而且我已经找到了解决这个问题的可能方法,所以我决定发布它
我没有像在问题中那样尝试对任何容器部分地专门化标记
,而是假设拥有一个包含元素的容器是一种一般情况。因此,标记
模板将类型
导出为一个集合。如果类型
不满足此条件,模板演绎将选择另一个适合的类型
专门化:一个用于单个元素。通过引入集合
结构来施加该条件。任何具有value\u type
可用的容器现在都被识别为标记元素的集合
以下是解决方案(我只是将类型的名称更改为水果名称,我想是为了使其更易于阅读):
编译器产生了相当准确的诊断:这种struct标记的“专门化”并没有真正使用它的模板参数。@构造函数,是的,这就是我所想的,但问题仍然存在:如何专门化包含标记元素的所有容器的tag
?现在我正试图使用value\u type
来实现这一点,并指望使用模板推断机制,但我仍然失败了。。
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);
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;
};
}
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);
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’
// 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;
//};
//};
#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