【发布时间】:2023-03-14 04:19:01
【问题描述】:
情景:
假设我们有几个库,每个库都在自己的命名空间中,每个库都包含完全相同的类和函数集,并且在每个命名空间中都具有相同的 API。这些库完全不相互了解,也没有共同的基础。
namespace A {
class Foo { ... };
class Bar { ... };
class Baz { ... };
// A dozen more types
};
namespace B {
class Foo { ... };
class Bar { ... };
class Baz { ... };
// A dozen more types
};
namespace C {
class Foo { ... };
class Bar { ... };
class Baz { ... };
// A dozen more types
};
我绝不为这种设计辩护,但让我们将其视为无法更改的约束。显然有更好的方法可以做到这一点,但我现在无权改变它。
问题:
鉴于上述情况,我们通常希望实现使用这些库的更高级别的代码,但我们不想为每个库都重复此代码。模板似乎是显而易见的解决方案。
template<typename Foo, typename Bar, typename Baz>
class FooBarBazzer
{
std::unique_ptr<Foo> foo;
std::vector<Bar> bars;
Baz baz;
...
}
这看起来还不错,但它要求我们将Foo、Bar 和Baz 列为模板参数,并且随着涉及的类型数量的增加,这很快导致了一些非常笨拙的模板参数列表。
我发现自己想要以下虚构的语法:
template<namespace ns>
class FooBarBazzer
{
std::unique_ptr<ns::Foo> foo;
std::vector<ns::Bar> bars;
ns::Baz baz;
...
}
一种可能的解决方法是为每个命名空间编写和维护一个特征struct,其中包含该命名空间中每个类型的成员类型。
namespace A {
struct Types {
using Foo = A::Foo;
using Bar = A::Bar;
using Baz = A::Baz;
// A dozen more type aliases
};
}
namespace B {
struct Types {
using Foo = B::Foo;
using Bar = B::Bar;
using Baz = B::Baz;
// A dozen more type aliases
};
}
namespace C {
struct Types {
using Foo = C::Foo;
using Bar = C::Bar;
using Baz = C::Baz;
// A dozen more type aliases
};
}
template<typename Types>
class FooBarBazzer
{
std::unique_ptr<typename Types::Foo> foo;
std::vector<typename Types::Bar> bars;
typename Types::Baz baz;
...
}
但这似乎有很多样板。 (还有很多 typename 关键字散布在周围,这并不理想,但我怀疑这是可以避免的。)
问题:
有没有更好的技巧来做到这一点?在实例化时告诉模板应该在哪个命名空间中查找类型?一个不需要编写和维护太多样板的?
我怀疑答案是否定的,但我一直对专家能够让 C++ 类型系统做的事情感到惊讶。
【问题讨论】:
-
根据您对
DoAllFoosBar的实际 impl 的外观,我们或许可以避免使用参数包而不是显式列出所有类型 -
写一些类型特征可能会有所帮助。
-
不,你不能这样做。模板适用于类型,而不是命名空间。如果你想要一个“上帝”类型,你必须定义一个服务于这个目的的类型(你自己提到这个)。你也可以使用宏,如果你愿意的话。
-
简短的回答是不,没有办法将命名空间传递给模板。但是,正如您在问题中所承认的那样,这似乎非常适合仅利用 ADL。您是否有任何理由对仅使用 SFINAE 约束
DoAllFoosBar而不是尝试约束到整个命名空间感到不满?
标签: c++ templates namespaces