【发布时间】:2015-01-05 17:45:18
【问题描述】:
我最近在编写一些类似库的代码时遇到了这个问题,我认为讨论它也可能对其他人有所帮助。
假设我有一个在命名空间中定义了一些函数模板的库。函数模板适用于客户端代码提供的类型,它们的内部工作可以根据为客户端类型定义的类型特征进行定制。所有客户端定义都在其他命名空间中。
对于最简单的示例,库函数基本上必须如下所示(注意所有代码 sn-ps 只是一厢情愿,没有任何编译):
namespace lib
{
template<typename T> void f()
{
std::cout << traits_for<T>::str() << '\n'; //Use the traits in some way.
}
}
客户端代码如下所示:
namespace client
{
struct A { };
template<> std::string traits_for<A>::str() { return "trait value"; }
}
然后有人,某处可以打电话
lib::f<client::A>();
并且一切都会神奇地工作(lib::f() 的特化会在声明 T 的模板参数的命名空间中找到特征显式特化,就像 ADL 对函数及其参数所做的那样)。目标是让客户端代码尽可能轻松地为每个客户端类(可能有很多)定义这些特征(可能有几个)。
让我们看看我们可以做些什么来完成这项工作。显而易见的事情是在lib 中定义一个traits 类主模板,然后显式地将其专门用于客户端类型。但是客户不能在他们自己的命名空间中定义那些显式的特化;他们必须退出它,至少到全局命名空间,定义明确的特化,然后重新进入 client 命名空间,为了获得最大的乐趣,可以嵌套。我想将特征定义保持在每个客户端类定义附近,因此必须在每个类定义附近完成这个命名空间杂耍。突然,客户端代码中的单行代码变成了杂乱无章的多行代码;不好。
为了允许在 client 命名空间中定义特征,我们可以将特征类转换为特征函数,可以像这样从 lib 调用:
traits_for(T())
但是现在我们正在创建一个 T 类的对象,只是为了让 ADL 发挥作用。这样的对象构建起来可能很昂贵(在某些情况下甚至是不可能的),所以这也不好。我们必须继续只使用类型,而不是它们的实例。
放弃并将特征定义为客户端类的成员也不是一种选择。
完成这项工作所需的一些管道是可以接受的,只要它不会使 client 命名空间中每个类和特征的定义复杂化(编写一些代码一次,但不是为每个定义编写)。
我找到了满足这些严格要求的解决方案,我会将其写在答案中,但我想了解人们对此的看法:替代方案、对我的解决方案的批评、cmets 关于如何所有这一切要么明显流血,要么在实践中完全没用,工作......
【问题讨论】:
-
@sbabbi 不错的发现。相同的技术应用于略有不同且更复杂的环境中。我能说什么......伟大的思想都一样:-)
-
为什么不简单地nest the traits within the client class?如果您的目标只是从类型映射到类型,那么成员类型(或 typedef)就非常简单。
-
@Casey 确实,我没有给出任何不想要的理由,问题只是说“这不是一个选择”,但我认为这是一个合理的要求。通过单独的特征,
lib代码也可以以统一的方式为内置类型定制。此外,客户端类可以在应用程序的多个不同上下文中使用;与lib的接口只是其中之一。保持核心类定义尽可能简单并且不将它们绑定到特定库代码的要求(如果可能的话)是使用单独特征类的另一个原因。
标签: c++ templates namespaces argument-dependent-lookup