【问题标题】:BOOST_CHECK_EQUAL with pair<int, int> and custom operator <<BOOST_CHECK_EQUAL 与 pair<int, int> 和自定义运算符 <<
【发布时间】:2012-06-11 07:41:22
【问题描述】:

当尝试做一个 BOOST_CHECK_EQUAL(pair, pair) 时, gcc 没有找到 pair 的流操作符,尽管声明了它。 有趣的是 std::out 找到了操作符。

ostream& operator<<(ostream& s, const pair<int,int>& p) {
    s << '<' << p.first << ',' << p.second << '>';
    return s;
}


BOOST_AUTO_TEST_CASE(works)
{
    pair<int,int> expected(5, 5);
    pair<int,int> actual  (5, 5);
    std::cout << expected << std::endl;
    std::cout << actual   << std::endl;
    BOOST_CHECK(actual == expected);
}

BOOST_AUTO_TEST_CASE(no_work)
{
    pair<int,int> expected(5, 5);
    pair<int,int> actual  (5, 5);
    BOOST_CHECK_EQUAL(actual, expected);
}

这不会编译错误:

...  instantiated from here
../boost-atp/release/include/boost/test/test_tools.hpp:326:9: error: no match for ‘operator<<’ in ‘ostr << t’

【问题讨论】:

  • 这里解释了为自定义类型定义自己的打印的方法:stackoverflow.com/a/44810846/1617295this is the official documentation 该功能。
  • @Raffi 这个问题看起来像这个问题的副本,细节较少。也许将其标记为重复并将您的答案移到此处是有意义的,这样我们就不会让用户跳来跳去?我也可以接受。谢谢!

标签: c++ boost boost-test


【解决方案1】:

Remus's answer 一样将operator&lt;&lt; 放入std 是C++ 14 草案(N4296 部分:17.6.4.2.1)中未定义的行为。 Boost 提供了一个钩子(used by this answer),你可以这样写:

namespace boost
{
    namespace test_tools
    {
        template<typename T,typename U>
        struct print_log_value<std::pair<T, U> >
        {
            void operator()(std::ostream& os, std::pair<T, U> const& pr)
            {
                os << "<" << std::get<0>(pr) << "," << std::get<1>(pr) << ">";
            }
        };
    }
}

print_log_value 是一个模板,因此如果您没有声明像 pair&lt;T,U&gt; 这样的模板值,则需要编写如下内容:

template<>
struct print_log_value<MyType>{ /* implementation here*/ };

编辑

如果您使用的是 boost 1.59 或更高版本,则需要改用命名空间 boost::test_tools::tt_detail。也就是代码需要启动:

namespace boost
{
    namespace test_tools
    {
        namespace tt_detail
        {

【讨论】:

  • 啊!那太棒了!这样干净多了。
【解决方案2】:

试着把operator itself in the std namespace:

namespace std
{
  ostream& operator<<(ostream& s, const pair<int,int>& p) {
    s << '<' << p.first << ',' << p.second << '>';
    return s;
  }
}

更新:也许这就是ADL fails(至少在 llvm 上)的原因:

就像以前一样,不合格的查找没有找到任何声明 名称operator&lt;&lt;。与以前不同,参数类型都包含 类类型:其中之一是类模板类型的实例 std::basic_ostream,另一个是我们的类型ns::Data 上面声明的。因此,ADL 将在命名空间stdns 中查找 对于operator&lt;&lt;。由于其中一种参数类型仍然依赖 在模板定义期间,ADL 直到模板完成后才完成 在使用期间实例化,这意味着我们希望它的operator&lt;&lt; find 已经被声明了。不幸的是,它被宣布在 全局命名空间,不在 ADL 将查找的任何一个命名空间中 进来!

【讨论】:

  • 我不明白为什么 ADL 不适用于 BOOST_CHECK_EQUAL。 Boost 是否会采取措施阻止这种情况的发生?
  • @njr:我前段时间调查过,但没有找到根本原因。 Afaik gcc 做 ADL,llvm 也是。
  • 这对我有用,但它似乎是未定义的行为(en.cppreference.com/w/cpp/language/extending_std)。看,我发现了以下答案,可以避免未定义的行为stackoverflow.com/a/17573165/309334
  • 好消息:给出的示例不在 C++14 草案标准中(N4296 部分:17.6.4.2.1)。 坏消息:标准的措辞与您引用的相似。标准中列出的唯一允许的内容是模板特化。 ostream&amp; operator&lt;&lt;(ostream&amp;, pair&lt;int,int&gt;) 不是模板特化,它是函数重载。
  • 措辞是:除非另有说明,否则如果将声明或定义添加到命名空间 std 或命名空间 std 内的命名空间,则 C++ 程序的行为是未定义的。只有当声明依赖于用户定义的类型并且特化满足原始模板的标准库要求并且没有明确禁止时,程序才能将任何标准库模板的模板特化添加到命名空间 std。
【解决方案3】:

我一直在寻找类似的东西,一种自定义输出字符串以打印十六进制整数的方法。将运算符注入 std 命名空间会起作用,但我测试中的每个 BOOST_CHECK 都会以十六进制打印。

所以我在 boost 命名空间中注入了一些自定义运算符,我可以使用一些全局布尔值来控制它们。

在这里查看我的答案boost-check-fails-to-compile-operator-for-custom-types

【讨论】:

    猜你喜欢
    • 2015-08-18
    • 2020-10-22
    • 2019-02-20
    • 1970-01-01
    • 2017-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多