c++ - 有没有让我们将 "pass a namespace"放入模板的技巧?

标签 c++ templates namespaces

场景:
假设我们有几个库,每个库都在自己的命名空间中,每个库都包含完全相同的一组类和函数,在每个命名空间中具有相同的 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 , BarBaz随着模板参数和涉及的类型数量的增加,这很快导致了一些非常笨拙的模板参数列表。
我发现自己想要以下虚构的语法:
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++ 类型系统所做的事情感到惊讶。

最佳答案

简答
没有办法通过namespacetemplate ;但这并不意味着您的情况没有其他选择。
长答案
虽然没有办法通过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/

相关文章:

调用共享 C++ 函数时 Python 内核崩溃

c++ - 在匿名命名空间中声明的常量与函数范围静态常量相比有哪些(缺点)优势?

c++ - 为什么成员模板方法必须在类外使用 template<> 进行专门化

c++ - 定义不区分大小写的映射模板

c++ - 如何专门化 T 的特征类以及 T 的所有后代

php - 为什么::class 附加到 Laravel 5.1 中的 PHP 类

PHP - 未找到命名空间,使用 Composer 自动加载项目

c++ - 通过剥离变量宏/模板/函数对来测试成员函数?

c++ - 如何从文件中的特定行读取 (C++)

c++ - fread/fwrite 大小和计数