【问题标题】:C++ STL fails to find comparator for nested classC++ STL 找不到嵌套类的比较器
【发布时间】:2013-04-18 04:44:25
【问题描述】:

我希望这段代码能够工作,但它无法使用 GCC 编译。如果您将内部类取出,它确实可以编译。

#include <algorithm>

template <typename T>
struct Outer
{
  struct Inner
  {
    int x;
  };
  Inner vec[3];
};

template <typename T>
bool operator <(const typename Outer<T>::Inner& lhs, const typename Outer<T>::Inner& rhs)
{
  return lhs.x < rhs.x;
}

int main()
{
  Outer<int> out;
  Outer<int>::Inner in;
  std::lower_bound(out.vec, out.vec + 3, in);
}

GCC 4.4 有这样的说法:

...
bits/stl_algo.h:2442: error: no match for ‘operator<’ in ‘* __middle < __val’

GCC 4.7 打印了更多内容,包括上述内容,以以下结尾:

...
bits/stl_algobase.h:957:4: note: couldn't deduce template parameter ‘T’

我愿意相信它不是格式良好的 C++,但为什么不呢?

【问题讨论】:

    标签: c++ stl inner-classes nested-class name-lookup


    【解决方案1】:

    您提到的问题是编译器无法推断出模板参数T。这是因为 typename Outer::Inner 是 T 的 非推导上下文 上下文。

    当模板参数仅在非推导上下文中使用时,模板参数推导不会考虑它。详细信息在 C++ 标准 (2003) 的第 14.8.2.4 节中。

    为什么?如果您将 Outer 专业化为:

    template <>
    struct Outer<int>
    {
       struct Inner
       {
           double y;
       };
       Inner vec[3];
    }; 
    

    编译器无法推断 Outer::Inner 是否应该引用此定义或前一个定义。

    现在,解决方案。有几种可能的解决方案:

    1. 将运算符
    2. Define operatorM M. 建议的那样。
    3. Johannes Schaub - litb 建议:从由内部参数化的 CRTP 基派生内部。然后将参数类型设为 CRTP 类,并将参数引用强制转换为派生的内部类,其类型由您推导出的模板参数给出。

    我尝试了CRTP 方法,因为它听起来很酷!:

    template <typename Inner>
    struct Base
    {
    };
    
    template <typename T>
    struct Outer
    {
      struct Inner : Base<Inner>
      {
        T x;
      };
      Inner vec[3];
    };
    
    template <typename T>
    bool operator< (const Base<T>& lhs, const Base<T>& rhs)
    {
      return static_cast<const T&>(lhs).x < static_cast<const T&>(rhs).x;
    }
    

    相关答案:123

    【讨论】:

    • 我喜欢你的回答,即使我不喜欢它对 C++ 的意义。感谢您抽出宝贵的时间——您肯定赢得了赏金!
    【解决方案2】:

    这是另一种解决方法。 为什么不使用自定义比较器?

    template <typename T>
    struct Comparer
    {
        bool operator()(const typename Outer<T>::Inner& lhs, const typename Outer<T>::Inner& rhs)
        {
            return lhs.x < rhs.x;
        }
    };
    
    int main()
    {
        Outer<int> out;
        Outer<int>::Inner in;
        std::lower_bound(out.vec, out.vec + 3, in, Comparer<int>());
    }
    

    希望这对你有用。

    【讨论】:

    • 我可以将其用作解决方法;我仍然想知道为什么我的问题中的代码不能“正常工作”。
    【解决方案3】:

    如果您为int 重载特定的operator&lt;,问题就会消失:

    bool operator<(const typename Outer<int>::Inner& lhs, 
                   const typename Outer<int>::Inner& rhs)
    {
        return lhs.x < rhs.x;
    }
    

     

    更简单的解决方案是在Inner 中定义operator&lt;

    template<typename T>
    struct Outer
    {
    
        struct Inner
        {
            int x;
    
            bool operator<(const Inner& obj) const
            {
                return x < obj.x;
            }
    
        };
        Inner vec[3];
    };
    

    另外,这只是一个快速的解决方案。而我的答案不是为什么编译器在模板模式下嵌套的情况下找不到operator&lt;

    【讨论】:

    • 哦,天哪,我发布的代码被简化了太多,无法显示整个问题。让我再试一次,确保这里确实存在问题,而不仅仅是我无法制作正确的测试用例!谢谢。
    • 好的,我已经更新了问题中的代码以更好地反映问题。你能再看看吗?
    • @JohnZwinck:我刚刚根据您的新编辑更新了我的答案,以使其具有相关性。
    【解决方案4】:

    Mar0ux 的回答非常好。您可以在此处找到更多信息:

    Stephan T. Lavavej: Core C++, 2 of n

    您应该观看整个视频系列 - 它包含许多有用的信息,但您可以从第 34 分钟左右开始回答您的问题。 Stephen 提到了一个需要牢记的基本规则:

    ::是模板实参推演的砖墙,即不能推演左侧的模板实参T

    【讨论】:

    • 我也很喜欢这个答案——尤其是“:: 是模板参数推导的砖墙”,很好很清楚。
    猜你喜欢
    • 2023-04-05
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 2023-02-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多