【问题标题】:Specialization of 'template<class _Tp> struct std::less' in different namespace'template<class _Tp> struct std::less' 在不同命名空间中的特化
【发布时间】:2010-02-17 16:19:46
【问题描述】:

我专门针对数据类型使用“少”(谓词)。

代码如下所示:

template<>
struct std::less<DateTimeKey>
{
   bool operator()(const DateTimeKey& k1, const DateTimeKey& k2) const
   {
      // Some code ...
   }
};

编译时(Ubuntu 9.10 上的 g++ 4.4.1),我收到错误:

'template struct std::less' 在不同命名空间的特殊化

我做了一些研究,发现有一个“解决方法”涉及将专业化包装在 std 命名空间中 - 即将代码更改为:

namespace std {
template<>
struct less<DateTimeKey>
{
   bool operator()(const DateTimeKey& k1, const DateTimeKey& k2) const
   {
      // Some code ...
   }
};
}

这确实会关闭编译器。但是,该解决方案来自一个 5 岁的帖子(由“伟大的”维克多·巴扎罗夫(Victor Bazarof)提出[双关语无意])。这个修复仍然是要走的路,还是有更好的方法来解决这个问题,或者“旧方法”仍然有效?

【问题讨论】:

  • 过载DateTimeKey::operator&lt;?

标签: c++


【解决方案1】:

这仍然是这样做的方法。不幸的是,您不能像使用类那样在命名空间中声明或定义函数:您需要将它们实际包装在命名空间块中。

【讨论】:

    【解决方案2】:

    如果您需要专门化标准算法,可以在 std 命名空间中进行。根据标准,这是唯一允许您在该命名空间内执行的操作。

    [lib.reserved.names]/1

    对于 C++ 程序来说,它是未定义的 添加声明或定义到 命名空间标准或命名空间内 命名空间 std 除非另有说明 指定的。一个程序可以添加模板 任何标准的专业化 库模板到命名空间 std。 这样的专业化(完整或 部分)标准库 模板导致未定义的行为 除非声明依赖于 自定义外部链接名称 除非专业满足 标准库要求 原始模板

    现在,问题是您是否真的想专攻std::less。请注意,std::less 将调用为您的类型定义的比较运算符,因此您可以提供该操作而不是专门化模板。

    std::less 专门用于您的特定类型的问题在于,如果您提供的操作与operator&lt; 为您的类型执行的操作不同,则会导致混淆。如果他们执行相同的操作,只需保留默认的 std::less 定义而不进行专门化。

    如果您不想提供比较运算符,但仍想在关联容器中使用类型或使用需要比较器的算法,您可以提供一个不会混淆其他读者的其他名称的外部比较函子(和你自己在未来的某个地方)。

    【讨论】:

      【解决方案3】:

      你为什么要这样做?

      std::less 仅用于两个目的:

      1. 给运算符
      2. 明确允许比较不在同一个数组中的两个指针(如果使用原始指针,这在技术上是非法的)

      用户没有理由重载它 - 要么重载 operator&lt;,要么使用自定义比较器函数。

      有一些标准算法可以被合理地特化 - std::swap 是一个很好的例子 - 这样做你需要在命名空间std中声明特化。

      【讨论】:

      • 我不确定 std::swap 是一个很好的例子,因为聪明的钱是你不专门化它,你在与 UDT 相同的命名空间中定义一个独立的 swap 函数,并让 ADL 找到它。我能想到的例外情况是,如果您的班级必须使用从未收到该备忘录的模板,并且该模板调用std::swap(t1,t2) 而不是using std::swap; swap(t1,t2);。不确定这是明智的专业化,还是仅仅是务实的专业化;-)
      • 关于哪个是最好的 boost 邮件列表已经有很多讨论,这就是为什么 boost::swap 也选择了。
      • std::less 存在的第三个原因:有时,一个类型的 operator&lt; 行为有点奇怪。最好的例子是浮点数,人们可能希望将-0.00.0 区别对待,或者可能希望使nan 具有可比性。其他示例是仅具有部分顺序的类型,其中&lt; 表示法是自然的特定于域的表示法。在这些情况下,专门化 std::less 允许在集合和地图中使用此类型。
      • @Erik Schnetter:在这些情况下,您应该使用自定义比较器。将 std::less 专门化为不像 operator &lt; 那样行事违反了最小意外原则。
      • @JoeGauterin en.cppreference.com/w/cpp/utility/functional/less> 说:“std::less 对任何指针类型的部分特化产生了一个总顺序,即使内置运算符
      【解决方案4】:

      即使其他人已经回答了有关如何专门化 std::less(通过将它们包装在命名空间块中)和正确的方法(重载 operator &lt;)的答案。

      但是,C++ 现在允许(在C++11 中)指定您在第一个示例中所做的方式。

      应在包含专用模板的命名空间中声明显式专用化。 declarator-id 不合格的显式特化应在模板的最近的封闭命名空间中声明,或者,如果命名空间是内联的(7.3.1),则应在其封闭命名空间集中的任何命名空间中声明。这样的声明也可以是一个定义。如果声明不是定义,则可以稍后定义特化(7.3.1.2)。

      我在Ubuntu 机器上使用g++ (8.3.0) 尝试了以下代码。

      #include <iostream>
      
      #include <map>
      #include <string>
      #include <algorithm>
      
      
      class myType {
      
      public:
          myType(int in): i_(in) { }
      
          int i_;
      };
      
      template <>
      struct std::less<myType>  {
          bool operator()(const myType& a, const myType& b) const
          {
              return a.i_ < b.i_;
          }
      };
      
      
      int main(int argc, char *argv[])
      {
          std::map<myType, std::string> vector = { { 1, "1"}, { 2, "2"}, { 3, "3"}, { 0, "0" } };
      
          for (auto& i: vector)
              std::cout << i.first.i_ << std::endl;
          return 0;
      }
      

      上面的代码是用

      编译的
      g++ --std=c++11 compare.cpp -Wall 
      

      【讨论】:

        【解决方案5】:

        less 函子不必位于 std 命名空间中。所以

        struct A
        {
            A(int _v=0):v(_v){}
            int v;
        };
        
        
        template<>  struct less<A>
        {
            bool operator()(const A& k1, const A& k2) const
            {
                return k1.v < k2.v;
            }
        };
        
        
        std::map<A,int> m;
        m[A(1)] = 1;
        m[A(2)] = 2;
        

        按预期工作。 (调用您刚刚创建的函子)。

        我想你已经知道了,但你可以编写自己的运算符

        bool operator<(const DateTimeKey & k1, const DateTimeKey & k2)
        {
        //your code...
        }
        

        【讨论】:

        • 不,它必须在标准中。 less 结构没有主模板。叮当说error: no template named 'less'; did you mean 'std::less'?
        • 是的,必须在 std:: 中。要么将其包装在 std:: 中,要么如上所述使用 std::less。
        猜你喜欢
        • 1970-01-01
        • 2022-12-02
        • 2014-11-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-02
        相关资源
        最近更新 更多