场景:
假设我们有几个库,每个库都在自己的命名空间中,每个库都包含完全相同的一组类和函数,在每个命名空间中具有相同的 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++ 类型系统所做的事情感到惊讶。
最佳答案
简答
没有办法通过namespace
到 template
;但这并不意味着您的情况没有其他选择。
长答案
虽然没有办法通过namespace
具体来说,也没有真正的理由为什么您不能以通用方式支持您的用例。您使用特征的建议肯定会奏效——但可能是不必要的手动工作,具体取决于您的问题的具体情况。
您提到了您对类型数量增长的担忧——这可以通过可变参数列表来满足:
template<typename Foo, typename Bar, typename...Args>
bool DoAllFoosBar(const std::vector<Foo> & foos, const Bar & bar, const Args&...args)
{
return std::all_of(foos.begin(),
foos.end(),
[&](const auto & foo){ return IsFooBar(foo, bar, args...); });
}
只要其中 1 个参数与 namespace
中的某些内容明确匹配(从它的声音来看,它应该),它仍然会通过 ADL 限定调用——它刚刚支持两个或更多参数。此外,尽管您不能严格限制 namespace 本身——您可以将函数限制为仅在
IsFooBar
时才可调用。在其论点上是可调用的——通过 SFINAE 或仅 static_assert
.例如:
// Dummy definition in current scope, so it can find others through ADL
void IsFooBar();
template<typename Foo, typename Bar, typename...Args>
auto DoAllFoosBar(const std::vector<Foo> & foos, const Bar & bar, const Args&...args)
-> decltype(IsFooBar(foos, bar, args...))
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- expression sfinae
{
...
}
这使用 Expression SFINAE 有条件地启用 DoAllFoosBar
当且仅当 IsFooBar(...)
产生有效的结果。因为它也看到了 IsFooBar
在当前范围内,它还将通过 ADL 查找所有有效条目。你也可以做类似的事情把它变成 static_assert
以及。
关于c++ - 有没有让我们将 "pass a namespace"放入模板的技巧?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66458024/