gpt4 book ai didi

c++ - 自定义点和 ADL

转载 作者:太空狗 更新时间:2023-10-29 21:19:12 28 4
gpt4 key购买 nike

我正在编写一个库,并且有一个函数使用任意类型作为参数执行对自由函数 foo 的(非限定)调用:

namespace lib {

template <typename T>
auto libfunc(T && t)
{
return foo(std::forward<T>(t));
}

} // namespace lib

库的用户可以为他自己的类型编写 foo 的重载:

namespace app {

class Foo { };

template <typename> class Bar { };

int foo(Foo const &) { return 99; }

template <typename T>
char foo(Bar<T> const &) { return 'x'; }

} // namespace app

ADL 找到了正确的函数 foo,所以像这样的代码可以工作:

app::Foo foo;
app::Bar<void**> bar;

auto x = lib::libfunc(foo);
auto y = lib::libfunc(bar);

但是,如果我想编写适用于 std 命名空间类型的 foo 版本,则没有匹配的函数 foo发现除非我将 foo 放在 std 命名空间中,这是不允许的:

#ifdef EVIL
namespace std {
#endif

template <typename T>
double foo(std::vector<T> const & ) { return 1.23; }

#ifdef EVIL
} // namespace std
#endif

std::vector<int> vec;

lib::libfunc(vec); // Only works if EVIL is defined

是否可以更改代码,以便用户可以在不侵入其命名空间的情况下为类型启用功能 foo?我考虑过在 lib-namespace 中对类模板进行部分模板特化,但还有其他可能性吗?

最佳答案

我找到了解决这个问题的两个方法。两者都有缺点。

声明所有标准重载

让标准类型的重载通过正常查找找到。这基本上意味着在使用扩展功能之前声明所有这些。请记住:当您在函数模板中执行非限定调用时,正常查找发生在定义点,而 ADL 发生在实例化点。这意味着普通查找只会找到从模板编写位置可见的重载,而 ADL 会找到稍后定义的内容。

这种方法的好处是用户在编写自己的函数时不会发生任何变化。

缺点是您必须在只想定义扩展点的 header 中包含要为其提供重载的每个标准类型的 header ,并提供该重载。这可能意味着非常严重的依赖性。

添加另一个参数

另一种选择是将第二个参数传递给函数。这是它的工作原理:

namespace your_stuff {
namespace adl {
struct tag {}

void extension_point() = delete; // this is just a dummy
}

template <typename T>
void use_extension_point(const T& t) {
using adl::extension_point;
extension_point(t, adl::tag{}); // this is the ADL call
}

template <typename T>
void needs_extension_point(const T& t) {
your_stuff::use_extension_point(t); // suppress ADL
}
}

现在您基本上可以在程序的任何位置为 std(甚至全局或内置)类型提供重载,如下所示:

namespace your_stuff { namespace adl {
void extension_point(const std::string& s, tag) {
// do stuff here
}
void extension_point(int i, tag) {
// do stuff here
}
}}

用户可以为自己的类型编写这样的重载:

namespace user_stuff {
void extension_point(const user_type& u, your_stuff::adl::tag) {
// do stuff here
}
}

好处:有效。

缺点:用户必须将 your_stuff::adl::tag 参数添加到他的重载中。这可能会被许多人视为烦人的样板,更重要的是,当用户忘记添加参数时,会导致令人费解的“为什么它没有发现我的过载”问题。另一方面,该论点还清楚地将重载标识为履行契约(Contract)(作为扩展点),这在下一位程序员出现并将函数重命名为 extensionPoint(以符合命名约定)然后当事情不再编译时吓坏了。

关于c++ - 自定义点和 ADL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28070519/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com