我可以命名ostream <<运算符吗?

时间:2018-12-14 17:09:02

标签: c++ namespaces operator-keyword

namespacing operator overloads is considered good practice如前所述,所以这就是我要做的。

问题:只有在我不编译时才编译。我只是做错了,还是在不可能的情况下找到了例外?

这里是一个易于翻译的翻译单元:

// lib/halfseconds.h:
#include <chrono>

namespace lib {
    using halfseconds = std::chrono::duration<intmax_t, std::ratio<1, 2> >;
}

// lib/debug.h:
#include <ostream>

namespace lib {
    std::ostream& operator<<(std::ostream& o, lib::halfseconds halves)
    {
        double seconds = halves.count();
        seconds /= lib::halfseconds::period::den;
        o << seconds << 's';
        return o;
    }
}

// demo/main.cpp:
#include <iostream>

int main()
{
    lib::halfseconds threeHalvseconds(3);
    std::cout << threeHalvseconds << '\n'; // 1.5s
}

编译器怎么说? G ++版本8.2.1指出“操作符<<>不匹配”,并生成一张令人生畏的候选列表(208行)。我想这些都不相关,因为如果没有相关的错误,我不会得到这个错误。

2 个答案:

答案 0 :(得分:3)

这里的问题是,一旦将operator <<放在命名空间中,就应该明确告诉编译器您要使用该命名空间,否则它将被隐藏。

要么:

  • 在主内部使用lib :: operator <<< / li>
  • lib :: operator <<((std :: cout,threeHalvseconds)<<'\ n';

答案 1 :(得分:2)

您要尝试使用的原理称为Argument Dependent Lookup。如果我在同一名称空间中声明了一个函数和一个类型,则可以在名称空间之外一起使用它们,而不必指定该函数来自哪个名称空间:

//In MyClass.h
namespace foo {
    class MyClass { /* stuff */ }; 
}
//In doStuff.h
namespace foo {
    void doStuff(MyClass c) { /* stuff */ }
}

//in main.cc
int main() {
    foo::MyClass tom; //I'm bad with names
    doStuff(tom); //Here, we don't have to specify the namespace
}

这几乎是您的示例中发生的事情。区别在于halfseconds实际上不是在命名空间lib中声明的。因为halfseconds是别名,所以实际上是在std::chrono中声明的,并且当您将operator<<重载放在命名空间lib中时,编译器不会对其进行检查。

如何解决此问题

解决此问题的最简单方法是在名称空间lib中声明新类型:

// lib/halfseconds.h:
#include <chrono>

namespace lib {
    class halfseconds 
        : public std::chrono::duration<intmax_t, std::ratio<1, 2>> 
    {
       public:
        using Base = std::chrono::duration<intmax_t, std::ratio<1, 2>>;
        using Base::Base; //Use the constructor
    };
}

// lib/debug.h:
#include <ostream>

namespace lib {
    std::ostream& operator<<(std::ostream& o, lib::halfseconds halves)
    {
        double seconds = halves.count();
        seconds /= lib::halfseconds::period::den;
        o << seconds << 's';
        return o;
    }
}

// demo/main.cpp:
#include <iostream>

int main()
{
    lib::halfseconds threeHalvseconds(3);
    std::cout << threeHalvseconds << '\n'; // 1.5s
}

您可以在使用std::chrono::duration的任何地方使用它,它具有所有相同的功能,并且由于它是在lib命名空间中定义的,因此可以与{{1 }}命名空间,而不必加前缀lib