测试左移位运算符的presence [英] Testing for the presence of the left shift operator

查看:148
本文介绍了测试左移位运算符的presence的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图找到一个工作类型特征检测一个给定的类型有一个的std :: ostream的左移运算符重载(例如,使用<$互通C $ C>的std :: COUT 或的boost :: lexical_cast的)。我已经成功与的boost :: has_​​left_shift 除了其中类型是POD STL容器的情况下,或的std ::字符串类型。我怀疑这与无论是STL类型或运营商LT的专业做;&LT;功能。什么是一般识别类型的有效的左移运算符的std :: ostream的正确的方法?如果这是不可行的,是有关于POD的STL容器或std ::字符串类型?

检测左移操作符的重载一个单独的方法

在code以下显示了我目前正与合作,并演示了的boost :: has_​​left_shift 如何无法检测超载运营商的LT ;&LT; ,即使它被称为下一行的功能。该程序编译和GCC 4.5.1或更高版本以及3.1铿锵的作品。

要规避明显的反应,我曾尝试更换模板运营商的LT;&LT;具有特定版本的使用无济于事各类功能。我也试过(各种调整导致我在一个运营商的LT指着​​一个编译器消息常量性和L-价值/ r值符两种类型的各种组合;&LT; 超载与r值ostream的)。我也试着实现我自己的特质它充其量给我相同的结果的boost :: has_​​left_shift

预先感谢可以提供任何帮助。我也因此,如果这种行为发生的一个详尽的解释,以及如何解决作品可以包括AP大大preciate它。我伸展我的模板知识的限制,并很想了解为什么我会认为它这样做是行不通的。

 的#include&LT;串GT;
#包括LT&;矢量&GT;
#包括LT&;&iostream的GT;
#包括LT&;升压/ lexical_cast.hpp&GT;
#包括LT&;升压/ type_traits / has_​​left_shift.hpp&GT;使用命名空间std;结构点{
    INT X;
    诠释Ÿ;
    点(INT X,int y)对:X(X),Y(Y){}
    串getStr()const的{返回(+的boost :: lexical_cast的&LT;串GT;(X)+,+的boost :: lexical_cast的&LT;串GT;(Y)+); }
};ostream的&安培;运营商所述;≤(ostream的&放大器;流,常量点和放大器; p)的
{
    流&LT;&LT; p.getStr();
    返回流;
}模板&LT; typename的T&GT;
ostream的&安培;运营商的LT;≤(ostream的&安培;流,常量的std ::矢量&lt; T&GT;&安培; 5)
{
    流&LT;&LT; [;
    为(自动它= v.begin();!它= v.end(); ++吧)
    {
        如果(它!= v.begin())
            流&LT;&LT; ,;
        流&LT;&LT; *它;
    }
    流&LT;&LT; ];
    返回流;
}模板&LT; typename的T&GT;
无效打印(常量字符串和放大器;名,T&amp; T公司)
{
    COUT&LT;&LT;命名&LT;&LT; 已经左移=&LT;&LT;提高:: has_​​left_shift&LT; ostream的,T&GT; ::值&LT;&LT; ENDL;
    COUT&LT;&LT; T =&LT;&LT; T&LT;&LT; ENDL&LT;&LT; ENDL;
}诠释的main()
{
    COUT&LT;&LT; boolalpha;    INT I = 1;
    打印(INT,我);    字符串s =ASDF;
    打印(标准::字符串,S);    点p(2,3);
    打印(点,P);    矢量&lt;诠释&GT;六({1,2,3});
    打印(性病::矢量&lt;&INT GT;六);    矢量&lt;字符串&GT; VS({×,Y,Z});
    打印(性病::矢量&lt;标准::字符串&gt;中,VS);    矢量&lt;点和GT; VP({点(1,2),点(3,4),点(5,6)});
    打印(性病::矢量&lt;点和gt;中,VP);
}


解决方案

为什么它不工作是C ++有解决函数调用有时令人惊讶的(但有干劲)规则的原因。特别是,在空间,其中呼叫发生,在的参数(用于UDT)的命名空间,首先进行名称查找:如果具有匹配名称的功能(或者如果匹配内置的操作)被发现,它被选择(或者,如果一个以上的发现,进行过载分辨率)。

只有具有匹配名称的功能的参数命名空间中发现,父命名空间进行检查。如果具有匹配名称的函数被发现不过是不可行的解决呼叫,或如果呼叫是模糊的,编译器不会保存在父命名空间中找了找一个更好的或明确比赛的希望:更确切地说,其得出这样的结论是没有办法解决的呼吁。

此机制是很好的解释<一个href=\"http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-/Stephan-T-Lavavej-Core-C-1-of-n\">in史蒂芬T. Lavavej 并在这个古老的文章香草萨特这presentation。

在你的情况,检查这个操作符存在的功能是在升压命名空间。你的论点是无论是从 STD 命名空间(的ostream 字符串矢量),或者是吊舱( INT )。在 STD 命名空间,非活超载运营商的LT;&LT; 存在,所以编译器不打扰父(全局)的命名空间,在这里你超载定义仰视。它只会得出结论,在升压完成命名空间(模拟)调用,检查运营商的LT是否;&LT; 是定义不能得到解决。

现在的boost :: has_​​left_shift 很可能有车削,否则这将是一个编译错误变成一个失败的替代一些SFINAE机械,将分配静态变量。

更新:

答案原有的部分解释的为什么的这不起作用。现在让我们看看是否有一种方法为解决它。由于ADL的使用量和 STD 命名空间包含的非可行超载运营商的LT;&LT; 事实上的阻止试图解决的呼叫,人们可能会倾向于移动运营商的LT的存活的过载;从;&LT全局命名空间到 STD 命名空间。

唉,延长了 STD 命名空间(这是什么,如果我们要添加运营商的LT的新重载,我们会做;&LT; )的禁止由C ++标准。什么是允许的,而不是对专攻 STD 命名空间中定义(除非另有说明)的模板功能;然而,这并不能帮助我们在这里,因为没有模板,它的参数可以通过专业化矢量&lt;&INT GT; 。此外,函数模板不能被部分专业,这会使事情更加难以处理。

有一个最后的可能性,但:超载添加到命名空间,其中电话解决发生。这是某处Boost.TypeTraits的机械内部。尤其是,我们感兴趣的是命名空间在通话是由在名称。

在当前版本的库,这恰好是的boost ::详细:: has_​​left_shift_impl ,但我不知道怎么的移动的这是在不同的升压版本。

不过,如果你真的需要一个解决方法,您可以在命名空间中声明你的运营商:

 名字空间boost
{
    命名空间细节
    {
        命名空间has_left_shift_impl
        {
            ostream的&安培;运营商所述;≤(ostream的&放大器;流,常量点和放大器; p)的
            {
                流&LT;&LT; p.getStr();
                返回流;
            }            模板&LT; typename的T&GT;
            的std :: ostream的&放大器;运营商的LT;≤(的std :: ostream的和放大器;流,常量的std ::矢量&lt; T&GT;&安培; 5)
            {
                流&LT;&LT; [;
                为(自动它= v.begin();!它= v.end(); ++吧)
                {
                    如果(它!= v.begin())
                        流&LT;&LT; ,;
                    流&LT;&LT; *它;
                }
                流&LT;&LT; ];
                返回流;
            }
        }
    }
}

和事情会开始工作。

有一个的警告的虽然:虽然这编译并运行良好与GCC 4.7.2与预期输出。然而,锵3.2似乎要求在的boost ::细节:: has_​​left_shift_impl 的的 has_​​left_shift要定义的重载.HPP 头也包括在内。我相信这是一个错误。

I'm trying to find a working type trait to detect if a given type has a left shift operator overload for a std::ostream (e.g. interoperable with std::cout or boost::lexical_cast). I've had success with boost::has_left_shift except for the cases where the type is an STL container of POD or std::string types. I suspect this has to do with the specializations of either the STL types or the operator<< functions. What is the correct method for generically identifying types with a valid left shift operator for std::ostream? If that is not feasible, is there a separate method for detecting an overload of the left shift operator on STL containers of POD or std::string types?

The code below shows what I'm currently working with, and demonstrates how boost::has_left_shift fails to detect the overloaded operator<< function even though it is called on the next line. The program compiles and works in GCC 4.5.1 or higher and clang 3.1.

To circumvent the obvious responses, I have tried replacing the templated operator<< function with specific versions for the various types used to no avail. I've also tried various combinations of const-ness and l-value/r-value specifiers for the two types (various tweaks lead me to a compiler message pointing at an operator<< overload with a r-value ostream). I've also tried implementing my own trait which at best gives me the same results as boost::has_left_shift.

Thanks in advance for any help that can be provided. I would also greatly appreciate it if a thorough explanation of why this behavior is occurring and how the solution works can be included. I'm stretching the limits of my template knowledge and would love to learn why this does not work as I would think it does.

#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);
}

解决方案

The reason why it does not work is that C++ has sometimes surprising (but well motivated) rules for resolving a function call. In particular, name lookup is first performed in namespace where the call happens and in the namespace of the arguments (for UDTs): if a function with a matching name (or if a matching built-in operator) is found, it is selected (or if more than one are found, overload resolution is performed).

Only if no function with the matching name is found in the namespace of the arguments, the parent namespaces are checked. If a function with a matching name is found is but not viable for resolving the call, or if the call is ambiguous, the compiler will not keep looking up in parent namespaces with the hope of finding a better or unambiguous match: rather, it will conclude that there is no way to resolve the call.

This mechanism is nicely explained in this presentation by Stephan T. Lavavej and in this old article by Herb Sutter.

In your case, the function which checks for the existence of this operator is in the boost namespace. Your arguments are either from the std namespace (ostream, string, vector) or are PODs (int). In the std namespace, a non-viable overload of operator << exist, so the compiler does not bother looking up in the parent (global) namespace, where your overload is defined. It will simply conclude that the (simulated) call done in the boost namespace to check whether operator << is defined can't be resolved.

Now boost::has_left_shift is likely to have some SFINAE machinery for turning what would otherwise be a compilation error into a failed substitution, and will assign false to the value static variable.

UPDATE:

The original part of the answer explained why this does not work. Let's now see if there is a way to work around it. Since ADL is used and the std namespace contains a non-viable overload of operator <<, de facto blocking the attempt to resolve the call, one could be tempted to move the viable overloads of operator << from the global namespace into the std namespace.

Alas, extending the std namespace (and this is what we would do if we were to add new overloads of operator <<) is forbidden by the C++ Standard. What is allowed instead is to specialize a template function defined in the std namespace (unless stated otherwise); however, this doesn't help us here, because there is no template whose parameters can be specialized by vector<int>. Moreover, function templates cannot be partially specialized, which would make things much more unwieldy.

There is one last possibility though: add the overload to the namespace where the call resolution occurs. This is somewhere inside the machinery of Boost.TypeTraits. In particular, what we are interested in is the name of the namespace in which the call is made.

In the current version of the library, that happens to be boost::detail::has_left_shift_impl, but I am not sure how portable this is across different Boost versions.

However, if you really need a workaround, you can declare your operators in that namespace:

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;
            }
        } 
    } 
}

And things will start working.

There is one caveat though: while this compiles and runs fine with GCC 4.7.2 with the expected output. However, Clang 3.2 seems to require the overloads to be defined in the boost::details::has_left_shift_impl before the has_left_shift.hpp header is included. I believe this is a bug.

这篇关于测试左移位运算符的presence的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆