简体   繁体   English

g ++和clang ++使用运算符<()重载的不同行为

[英]g++ and clang++ different behaviour with operator<() overloading

Hello and sorry for my bad English. 你好抱歉我的英语不好。

For the purpose to practice with c++11, I'm trying to write a version of the class std::experimental::any ( http://en.cppreference.com/w/cpp/experimental/any ) adding something extra. 为了练习c ++ 11,我正在尝试编写std :: experimental :: any( http://en.cppreference.com/w/cpp/experimental/any )类的一个版本来添加一些东西额外。

Adding the operator<() I got a different behavior between g++ (4.9.2) and clang++ (3.5.0). 添加运算符<()我在g ++(4.9.2)和clang ++(3.5.0)之间有不同的行为。

The following is a shortened version of the class (and classes that uses), covering the minimum necessary, and a very small main() to trigger the problem. 以下是类(和使用的类)的缩短版本,涵盖了必要的最小值,以及一个非常小的main()来触发问题。

Sorry for the long code but I failed to make the example shorter. 很抱歉长代码,但我没能缩短示例。

#include <memory>
#include <iostream>
#include <type_traits>
#include <unordered_set>

namespace yans // yet another name space
 {
   class anyB  // base for any
    {
      public:

         virtual std::type_info const & typeT () const = 0;
         virtual bool isLess (anyB const *) const = 0;
    };

   template <typename T>
      class anyD : public anyB  // derived for any
       {
         private:

            T val;

            static std::type_info const & typeInfo ()
             { static auto const & ret = typeid(T); return ret; }

            template <typename U> // preferred version
               static auto lessF (U const & u1, U const & u2, int)
               -> decltype(  std::declval<U const &>()
                           < std::declval<U const &>())
                { return (u1 < u2); }

            template <typename U> // emergency version
               static auto lessF (U const &, U const &, ...) -> bool
                { throw std::runtime_error("no operator < for type "); }

         public:

            anyD (T const & v0)
               : val(v0)
                { }

            std::type_info const & typeT () const override final
             { return typeInfo(); }

            bool isLess (anyB const * pB0) const override final
             {
               auto pD0 = dynamic_cast<anyD<T> const *>(pB0);

               if ( nullptr == pD0 )
                  throw std::bad_cast();

               return lessF(val, pD0->val, 0);
             }
       };

   class any
    {
      private:
         template <class T>
            using sT = typename std::decay<T>::type;

         template <class T>
            using noAny
            = typename std::enable_if
            <false == std::is_same<any, sT<T>>::value, bool>::type;

         template <class T>
            using isCpCtr
            = typename std::enable_if
            <true == std::is_copy_constructible<sT<T>>::value,bool>::type;

         std::unique_ptr<anyB>  ptr;

         static std::type_info const & voidInfo ()
          { static auto const & ret = typeid(void); return ret; }

         bool opLess (any const & a0) const
          {
            return
                  type().before(a0.type())
               || (   (type() == a0.type())
                   && (false == empty())
                   && ptr.get()->isLess(a0.ptr.get()) );
          }

      public:

         template <typename T, typename = noAny<T>, typename = isCpCtr<T>>
            any (T && v0)
            : ptr(new anyD<sT<T>>(std::forward<T>(v0)))
             { }

         bool empty () const noexcept
          { return ! bool(ptr); }

         std::type_info const & type () const
          { return ( ptr ? ptr->typeT() : voidInfo()); }

         friend bool operator< (any const &, any const &);
    };

   bool operator< (any const & a0, any const & a1)
    { return a0.opLess(a1); }
 }

int main () 
 {
   try
    {
      yans::any  ai { 12 };
      yans::any  as { std::string("t1") };
      yans::any  au { std::unordered_set<int> { 1, 5, 3 } };

      std::cout << "ai < 13 ? " << (ai < 13) << '\n';
      std::cout << "as < std::string {\"t0\"} ? "
         << (as < std::string {"t0"}) << '\n';
      std::cout << "au < std::unordered_set<int> { 2, 3, 4 } ? "
         << (au < std::unordered_set<int> { 2, 3, 4 }) << '\n';
    }
   catch ( std::exception const & e )
    {
      std::cerr << "\nmain(): standard exception of type \""
         << typeid(e).name() <<"\"\n"
         << "  ---> " << e.what() << " <---\n\n";
    }

   return EXIT_SUCCESS;
 }

The idea, behind operator<(), is to return "true" if the type of the left operand is less than the type of the right (according typeid(T).before()) and, if the types match, to return the value returned by the comparison of contained values. 运算符<()后面的想法是,如果左操作数的类型小于右侧的类型(根据typeid(T).before()),则返回“true”,如果类型匹配,则返回包含值的比较返回的值。 I know it's a questionable solution, but I'm playing to learn. 我知道这是一个值得怀疑的解决方案,但我正在努力学习。

The trouble is that in the instances of class any could be included values of types without operator<(). 麻烦的是,在类的实例中,任何类型的值都可以包含没有运算符<()的类型。 In the example, an instance of the class std::unordered_set<int>. 在示例中,类std :: unordered_set <int>的实例。 Then I tried to develop a couple of overloaded (SFINAE) method lessF(); 然后我尝试开发一些重载(SFINAE)方法lessF(); the preferred one, when operator<() is available for the type T included, return the comparison value; 当运算符<()可用于包含的类型T时,首选的一个返回比较值; the emergency version, used when operator<() is unavailable, throw an exception. 当operator <()不可用时使用的紧急版本会抛出异常。

With the clang++, I get what I wish: the class anyD<std::unordered_set<int>> does not implement the preferred version of lessF and comparison generates an exception from the emergency version. 使用clang ++,我得到我想要的:类anyD <std :: unordered_set <int >>没有实现lessF的首选版本,比较会从紧急版本生成异常。

With the g++, on the contrary, the class anyD<std::unordered_set<int>> generates the preferential version invoking the operator<() on two instances of any, build on the two std::unordered_set<int> arguments of lessF() (the templated constructor of any isn't "explicit" defined), and, then, recursively invokes itself, going in loop and generating an error ("Errore di segmentazione", that is "segmentation fault"). 相反,使用g ++,类anyD <std :: unordered_set <int >>生成在any的两个实例上调用operator <()的优先版本,在lessF的两个std :: unordered_set <int>参数上构建()(任何的模板化构造函数不是“显式”定义的),然后,递归地调用自身,进入循环并生成错误(“Errore di segmentazione”,即“分段错误”)。

What I'd like to understand is: 我想了解的是:

  • According to the ISO c++11, it's correct the behavior of clang++ or the behaviour of g++? 根据ISO c ++ 11,它是正确的clang ++行为还是g ++的行为?

  • Can I prevent locally (only in lessF()), and without declaring "explicit" the template constructor of any(), that the comparison between two std::unordered_set<int> will turn itself in the confrontation between two any? 我可以在本地阻止(仅在lessF()中),并且不声明任何()的模板构造函数的“显式”,两个std :: unordered_set <int>之间的比较会在两个之间的对抗中自行转换吗? In other words: how can I prevent the preferential version of lessF() to be developed in anyD<std::unordered_set<int>>? 换句话说:如何防止在anyD <std :: unordered_set <int >>中开发lessF()的优先版本?

The following are the outputs of the two programs. 以下是两个计划的产出。

---- clang++ program output ----
ai < 13 ? 1
as < std::string {"t0"} ? 0
au < std::unordered_set<int> { 2, 3, 4 } ? 
main(): standard exception of type "St13runtime_error"
  ---> no operator < for type  <---

---- end output ----

---- g++ program output ----
ai < 13 ? 1
as < std::string {"t0"} ? 0
Errore di segmentazione
---- end output ----

I believe you have uncovered a bug in gcc (I have reproduced it in much shorter form here , stay tuned). 我相信你已经发现了gcc中的一个错误(我已经在这里以更短的形式复制了它,请继续关注)。

The problem is, if you look through the segmentation fault, you'll see that the operator< call for unordered_set<int> is infinitely recursive. 问题是,如果你仔细研究分段错误,你会发现operator< unordered_set<int>operator<是无限递归的。 This is because gcc actually considers bool operator<(const any&, const any&) to be a match. 这是因为gcc实际上认为bool operator<(const any&, const any&)是匹配的。 It shouldn't be at the point where you're calling it. 它不应该是你打电话的地方。

The easy fix is to simply ensure that operator<(const any&, const any&) is only found for any 's, regardless of what namespace you're in. Simply move the definition into the class: 简单的解决方法是简单地确保any人找到operator<(const any&, const any&) ,无论你在哪个命名空间。只需将定义移到类中:

class any {
    friend bool operator< (any const & a0, any const & a1) {
        return a0.opLess(a1);
    }
};

This is good practice anyway. 无论如何,这是一个好习惯。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 运算符重载clang ++和g ++不同的输出 - Operator overloading clang++ and g++ different output 继承模板化的operator = in C ++ 14:与g ++和clang ++的不同行为 - Inheriting templated operator= in C++14: different behaviour with g++ and clang++ g ++和clang ++使用静态成员的递归初始化的不同行为 - g++ and clang++ different behaviour with recursive initialization of a static member g ++和clang ++使用变量模板和SFINAE的不同行为 - g++ and clang++ different behaviour with variable template and SFINAE g ++和clang ++使用指向可变参数模板函数的指针的不同行为 - g++ and clang++ different behaviour with pointer to variadic template functions g ++和clang ++使用自动参数的模板特化的不同行为 - g++ and clang++ different behaviour with template specialization for auto argument g++ 和 clang++ 与可变参数容器的不同行为 - g++ and clang++ different behaviour with variadic container 带有集成模板参数的g ++和clang ++不同的行为 - g++ and clang++ different behaviour with integral template parameter g ++和clang ++与流输入和无符号整数的不同行为 - g++ and clang++ different behaviour with stream input and unsigned integer g ++和clang ++与SFINAE和SFINAE失败的不同行为 - g++ and clang++ different behaviour with SFINAE and SFINAE failure
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM