gpt4 book ai didi

c++ - 测试是否存在左移运算符

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

我正在尝试找到一个工作类型特征来检测给定类型是否具有 std::ostream 的左移运算符重载(例如,可与 std::coutboost::lexical_cast 互操作)。我在 boost::has_left_shift 上取得了成功除了类型是 POD 或 std::string 的 STL 容器的情况类型。我怀疑这与 STL 类型或 operator<< 函数的特化有关。使用 std::ostream 的有效左移运算符一般识别类型的正确方法是什么? ?如果那不可行,是否有单独的方法来检测 POD 或 std::string 类型的 STL 容器上左移运算符的过载?

下面的代码显示了我目前正在使用的内容,并演示了如何 boost::has_left_shift未能检测到过载 operator<<函数,即使它在下一行被调用。该程序在 GCC 4.5.1 或更高版本和 clang 3.1 中编译和运行。

为了避免明显的 react ,我尝试替换模板化的 operator<<各种类型的特定版本的功能用于无济于事。我还为这两种类型尝试了 const-ness 和 l-value/r-value 说明符的各种组合(各种调整使我看到一条编译器消息,指向带有 r-value ostream 的 operator<< 重载)。我也尝试过实现我自己的特征,它最多给我与 boost::has_left_shift 相同的结果。 .

在此先感谢您提供的任何帮助。如果可以包含对为什么会发生这种行为以及解决方案如何工作的详尽解释,我也将不胜感激。我正在扩展我的模板知识的极限,很想知道为什么这不像我想象的那样有效。

#include <string>
#include <vector>
#include <iostream>
#include <boost/lexical_cast.hpp>
#include <boost/type_traits/has_left_shift.hpp>

using namespace std;

struct Point {
int x;
int y;
Point(int x, int y) : x(x), y(y) {}
string getStr() const { return "("+boost::lexical_cast<string>(x)+","+boost::lexical_cast<string>(y)+")"; }
};

ostream& operator<<(ostream& stream, const Point& p)
{
stream << p.getStr();
return stream;
}

template <typename T>
ostream& operator<<(ostream& stream, const std::vector<T>& v)
{
stream << "[";
for(auto it = v.begin(); it != v.end(); ++it)
{
if(it != v.begin())
stream << ", ";
stream << *it;
}
stream << "]";
return stream;
}

template <typename T>
void print(const string& name, T& t)
{
cout << name << " has left shift = " << boost::has_left_shift<ostream , T>::value << endl;
cout << "t = " << t << endl << endl;
}

int main()
{
cout << boolalpha;

int i = 1;
print("int", i);

string s = "asdf";
print("std::string", s);

Point p(2,3);
print("Point", p);

vector<int> vi({1, 2, 3});
print("std::vector<int>", vi);

vector<string> vs({"x", "y", "z"});
print("std::vector<std::string>", vs);

vector<Point> vp({Point(1,2), Point(3,4), Point(5,6)});
print("std::vector<Point>", vp);
}

最佳答案

它不起作用的原因是 C++ 有时有令人惊讶的(但动机良好的)解析函数调用的规则。特别是,名称查找首先在调用发生的命名空间和参数的命名空间(对于 UDT)中执行:如果找到具有匹配名称(或匹配的内置运算符)的函数,则选择它(或者如果找到多个,则执行重载决议)。

仅当在参数的命名空间中找不到具有匹配名称的函数时,才会检查父命名空间。如果找到具有匹配名称的函数但无法解析调用,或者调用不明确,编译器将不会继续在父命名空间中查找以期找到更好或明确的匹配:相反,它会结论是没有办法解决调用。

很好地解释了这种机制in this presentation by Stephan T. Lavavejthis old article by Herb Sutter .

在您的例子中,检查此运算符是否存在的函数在 boost 中命名空间。您的论点来自 std命名空间 ( ostream , string , vector ) 或 POD ( int )。在std命名空间,operator <<不可行 重载存在,因此编译器不会费心在定义重载的父(全局)命名空间中查找。它会简单地得出结论,在 boost 中完成的(模拟)调用命名空间检查是否operator <<已定义无法解析。

现在boost::has_left_shift很可能有一些 SFINAE 机制将编译错误转化为失败的替换,并将分配 falsevalue静态变量。

更新:

答案的原始部分解释了为什么这不起作用。现在让我们看看是否有办法解决它。由于使用了 ADL,并且 std命名空间包含不可行的重载 operator << , 事实上 阻止了解决调用的尝试,人们可能会想移动 operator <<可行 重载。从全局命名空间到 std命名空间。

唉,扩展std命名空间(如果我们要添加 operator << 的新重载,我们就会这样做)被 C++ 标准禁止。允许的是专门化 std 中定义的模板函数。命名空间(除非另有说明);然而,这对我们没有帮助,因为没有模板的参数可以被 vector<int> 专门化。 .此外,函数模板不能部分特化,这会使事情变得更加笨拙。

不过还有最后一种可能性:将重载添加到调用解析发生的命名空间。这是 Boost.TypeTraits 机制内部的某个地方。特别是,我们感兴趣的是进行调用的命名空间的名称

在当前版本的库中,它恰好是 boost::detail::has_left_shift_impl ,但我不确定这在不同的 Boost 版本中的可移植性如何。

但是,如果您真的需要解决方法,您可以在该命名空间中声明您的运算符:

namespace boost 
{
namespace detail
{
namespace has_left_shift_impl
{
ostream& operator<<(ostream& stream, const Point& p)
{
stream << p.getStr();
return stream;
}

template <typename T>
std::ostream& operator<<(std::ostream& stream, const std::vector<T>& v)
{
stream << "[";
for(auto it = v.begin(); it != v.end(); ++it)
{
if(it != v.begin())
stream << ", ";
stream << *it;
}
stream << "]";
return stream;
}
}
}
}

一切都会开始运作。

不过有一个警告:虽然它可以在 GCC 4.7.2 上正常编译和运行,并具有预期的输出。但是,Clang 3.2 似乎要求在 boost::details::has_left_shift_impl 中定义重载。 之前 has_left_shift.hpp包含 header 。我认为这是一个错误。

关于c++ - 测试是否存在左移运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14569768/

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