【问题标题】:C++11 conditional expression on std::greater and std::less got error of different typesstd::greater 和 std::less 上的 C++11 条件表达式出现不同类型的错误
【发布时间】:2016-09-28 16:35:00
【问题描述】:

以下与条件表达式相关的代码:

typedef unsigned char uchar;
uchar data[100];
// assign something to array[] here
uchar *start = data;
uchar *end = data+100;
bool cond = f();  // f() could return true or false
uchar *itr = std::upper_bound(start, end, uchar(20), 
                cond? std::greater<uchar>() : std::less<uchar>());

遇到这样的错误:

error: operands to ?: have different types 
‘std::greater<unsigned char>’ and ‘std::less<unsigned char>’

这是编译器错误吗?在我的直觉中,这两个函子应该具有相同的类型。

【问题讨论】:

  • 那是什么类型的?
  • 我刚刚将char *itr 更改为uchar *itr。我以为那是意图。 (这与主要问题无关,但编译这段代码很重要)
  • @Aaron:是的,这是一个错字。

标签: c++ c++11


【解决方案1】:

std::greaterstd::less 是不同的类型(就像 std::stringstd::unordered_map 一样不同)。它们只提供相同的接口 (operator())。

条件运算符cond ? expr1 : expr2 要求expr1expr2 属于同一类型(或者一个可以隐式转换为另一个),这不是您的情况。

你可以使用

char *itr;
if (cond)
    itr = std::upper_bound(start, end, uchar(20), std::greater<uchar>{});
else
    itr = std::upper_bound(start, end, uchar(20), std::less<uchar>{});

或者,如果您不想有两条几乎相同的行,您可以使用通用 lambda 来帮助自己

auto upper_bound = [&](const auto& cmp){
    return std::upper_bound(start, end, uchar(20), cmp);
};
if (cond)
    itr = upper_bound(std::greater<uchar>{});
else
    itr = upper_bound(std::less<uchar>{});

【讨论】:

  • 我只会使用auto&amp;&amp;,因为谁在乎它是否是const。而itr = cond?upper_bound(std::greater&lt;char&gt;{}):upper_bound(std::less&lt;char&gt;{}); 将更接近于 OP 的原始代码。
【解决方案2】:

C++ 中的函子传统上不共享一个类型。这使得编译器很容易准确地理解正在调用哪个函子,从而提高性能。

C++ 中有一种称为类型擦除的机制。在这里,您忘记了使两种类型不同的原因,而只记住了使它们相同的原因。

C++ 中最常见的类型擦除是std::function。此处的此函数类型擦除了一个无状态模板,该模板接受两个T const&amp; 并返回一个bool

template<class T, template<class...>class Z>
std::function< bool(T const&, T const&) >
comp() { return Z<T>{}; }

然后我们可以获取您的代码并执行此操作:

uchar *itr = std::upper_bound(start, end, uchar(20), 
            cond? comp<uchar, std::greater>() : comp<uchar, std::less>());

或者:

template<class T>
using comp_sig = bool(T const&, T const&);

template<class T, template<class...>class Z>
comp_sig<T>* comp() {
  return [](T const& lhs, T const& rhs)->bool{
    return Z<T>{}(lhs, rhs);
  };
}

它会根据需要为您提供调用lessgreater 的函数指针。对于编译器来说,这个版本可能比std::function 更容易优化,因为编译器非常擅长优化常量函数指针。

【讨论】:

    【解决方案3】:

    函子只是常规类型。 std::greater&lt;uchar&gt; 是与 std:less&lt;uchar&gt; 完全不同的类型。就像std::vector&lt;uchar&gt;std::set&lt;uchar&gt; 的不同之处一样,尽管它们的属性相似!

    如果你稍微移动你的三元运算符,你可以实现你想要实现的目标:

    char *itr = cond ?
        std::upper_bound(start, end, uchar(20), std::greater<uchar>()) :
        std::upper_bound(start, end, uchar(20), std::less<uchar>());
    

    它有点重复,但同样可读并且可以编译。

    【讨论】:

      【解决方案4】:

      我认为不是,因为据说它是不同的类型。 std::less 是这样的:

      template <class T>
      struct Less
      {
            bool operator()(const T & t1, const T & t2) ..
      };
      

      【讨论】:

      • 很好的答案,但鼓励解决 OP 的问题以及回答问题。
      【解决方案5】:

      首先,这两个函子的类型不同,尽管它们的operator()类型相同,也就是模板实例化后,

      bool operator()(uchar lhs, uchar rhs)  
      

      同样,vector&lt;int&gt;deque&lt;int&gt; 具有相同类型的 operator[] 显然并不意味着它们具有相同的类型。

      回到你的代码,如果你能够使用 C++11,一个简单而优雅的解决方案就是使用 lambda:

      char *itr = std::upper_bound(start, end, uchar(20),  
                  [cond](uchar lhs, uchar rhs) { return cond ? lhs > rhs : lhs < rhs); }
      

      否则,写一个简单的函子

      class F{  
      public:  
          explicit F(bool cond): m_cond(cond) {}
          bool operator(uchar lhs, uchar rhs) { return m_cond ? lhs > rhs : lhs < rhs); }  
      private:
          bool m_cond;
      };
      

      最后,lambda 是一个非常方便的工具,建议您多了解一下。
      What is a lambda expression in C++11?
      When to use functors over lambdas

      【讨论】:

        【解决方案6】:

        将它们都转换为 lambdas(非捕获,非泛型)。在这种情况下,它们的类型仍然不同,但它们都自然地转换为普通的旧函数指针 bool (*) (uchar, uchar),因此 ?: 有效

        uchar *itr = std::upper_bound(start, end, uchar(20),
                         cond?
                         ([](uchar x, uchar y){return std::greater<uchar>{}(x,y);})
                         :
                         ([](uchar x, uchar y){return std::less   <uchar>{}(x,y);})
                     );
        

        (这适用于 clang 3.5 和 g++ 5.3,但我不确定它的可移植性。我应该使用一元 + 强制转换为函数指针吗?

        【讨论】:

        • 这可能会在 MSVC 上中断,因为它们的 lambda 有 四个 到函数指针的隐式转换(每个调用约定一个),但我还没有测试过。
        猜你喜欢
        • 1970-01-01
        • 2013-01-27
        • 1970-01-01
        • 2012-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-12
        相关资源
        最近更新 更多