gpt4 book ai didi

c++ - 为什么我的特征模板类查找 operator<< 不能用于 llvm::StringRef?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:48:20 25 4
gpt4 key购买 nike

跟随问题How can I detect if a type can be streamed to an std::ostream?我写了一个特征类,说明是否可以将某种类型流式传输到 IO 流。直到现在我发现了一个问题,这个特性似乎运行良好。

我在一个使用 LLVM 的项目中使用代码,并且我正在使用他们的 StringRef 类(它在本质上类似于提议的 std::string_view)。 Here是该类的 Doxygen 文档的链接,如果需要,您可以从中找到它的声明头文件。由于 LLVM 不提供 operator<< 来将 StringRef 对象流式传输到标准流(它们使用自定义的轻量级流类),因此我编写了一个。

但是,当我使用该特征时,如果我的自定义运算符<<在该特征之后声明,则它不起作用(发生这种情况是因为我在一个 header 中具有该特征,并且 operator<< 函数在另一个)。我曾经认为模板实例化中的查找是从实例化点的角度来工作的,所以我认为它应该可以工作。实际上,正如您在下面看到的,使用另一个类及其自定义运算符 <<,在特征之后声明,一切都按预期工作(这就是为什么我现在才发现这个问题),所以我无法弄清楚是什么让 StringRef特别。

这是完整的例子:

#include <iostream>

#include "llvm/ADT/StringRef.h"

// Trait class exactly from the cited question's accepted answer
template<typename T>
class is_streamable
{
template<typename SS, typename TT>
static auto test(int)
-> decltype(std::declval<SS&>() << std::declval<TT>(),
std::true_type());

template<typename, typename>
static auto test(...) -> std::false_type;

public:
static const bool value = decltype(test<std::ostream,T>(0))::value;
};

// Custom stream operator for StringRef, declared after the trait
inline std::ostream &operator<<(std::ostream &s, llvm::StringRef const&str) {
return s << str.str();
}

// Another example class
class Foo { };
// Same stream operator declared after the trait
inline std::ostream &operator<<(std::ostream &s, Foo const&) {
return s << "LoL\n";
}

int main()
{
std::cout << std::boolalpha << is_streamable<llvm::StringRef>::value << "\n";
std::cout << std::boolalpha << is_streamable<Foo>::value << "\n";

return 0;
}

与我的预期相反,打印出:

false
true

如果我将 operator<< for StringRef 的声明移动到 trait 声明之前,它会打印 true。那么为什么会发生这种奇怪的事情,我该如何解决这个问题?

最佳答案

正如 Yakk 所提到的,这只是 ADL:参数相关查找。

如果您不想打扰,请记住您应该始终在与至少一个参数相同的命名空间中编写一个自由函数。在您的情况下,因为禁止向 std 添加功能,这意味着将您的函数添加到 llvm 中命名空间。事实上,您需要符合 StringRef 的条件与llvm::争论是一个致命的泄露。

函数解析的规则相当复杂,但作为一个速写:

  • 姓名查找:收集一组潜在候选人
  • 过载解决方案:在潜力中挑选最好的候选人
  • 特化解决方案:如果候选人是函数模板,请检查任何可以申请的特化

我们在这里关注的名称查找阶段相对简单。简而言之:

  • 它扫描参数的 namespace ,然后是它们的父级,...直到到达全局范围
  • 然后继续扫描当前作用域,然后是其父作用域,...直到到达全局作用域

可能 to allow shadowing (与任何其他名称查找一样),查找在遇到匹配项的第一个范围停止,并傲慢地忽略任何周围的范围。

请注意 using指令(例如 using ::operator<<;)可用于引入另一个范围的名称。虽然这很麻烦,因为它把责任推给了客户,所以请不要依赖它的可用性作为草率的借口(我已经看到这样做了:x)。


shadowing 的示例: 这会打印出 "Hello, World"不会引发歧义错误。

#include <iostream>

namespace hello { namespace world { struct A{}; } }

namespace hello { void print(world::A) { std::cout << "Hello\n"; } }

namespace hello { namespace world { void print(A) { std::cout << "Hello, World\n"; } } }

int main() {
hello::world::A a;
print(a);
return 0;
}

interrupted search 的示例: ::hello::world产生了一个名为 print 的函数所以它被挑选出来,即使它根本不匹配和::hello::print本来是一个更好的匹配。

#include <iostream>

namespace hello { namespace world { struct A {}; } }

namespace hello { void print(world::A) { } }

namespace hello { namespace world { void print() {} } };

int main() {
hello::world::A a;
print(a); // error: too many arguments to function ‘void hello::world::print()’
return 0;
}

关于c++ - 为什么我的特征模板类查找 operator<< 不能用于 llvm::StringRef?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23325797/

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