使用可转换为数组的类型来升级测试BOOST_CHECK_EQUAL

时间:2014-04-18 01:47:12

标签: c++ boost template-meta-programming c-strings boost-test

这是一个使用Boost测试的简单程序,其行为很奇怪":

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE foo
#include <boost/test/unit_test.hpp>

class C
{
public:
  C(char* str) : m_str(str) {}
  operator char*() const { return m_str; }
  char* get() const { return m_str; }

private:
  char* m_str;
};


BOOST_AUTO_TEST_CASE(test1)
{
  char s1[] = "hello";
  char s2[] = "hello";
  C c1(s1);
  C c2(s2);
  BOOST_CHECK_EQUAL(s1, s2); // check 1: passes
  BOOST_CHECK_EQUAL(c1, c2); // check 2: fails (why?)
  BOOST_CHECK_EQUAL(c1.get(), c2.get()); // check 3: passes
}

如果你运行它,它会在比较c1和c2时报告失败,当它似乎应该通过时。原因是这个代码在Boost测试中(我使用1.51):

// this is called for check 2
template <class Left, class Right>
predicate_result equal_impl( Left const& left, Right const& right )
{
    return left == right;
}

// this is called for checks 1 and 3
predicate_result        BOOST_TEST_DECL equal_impl( char const* left, char const* right );
inline predicate_result equal_impl( char* left, char* right )       { return equal_impl( static_cast<char const*>(left), static_cast<char const*>(right) ); }

// this decides which comparator to call
struct equal_impl_frwd {
    // this is called for checks 2 and 3
    template <typename Left, typename Right>
    inline predicate_result
    call_impl( Left const& left, Right const& right, mpl::false_ ) const
    {
        return equal_impl( left, right );
    }

    // this is called for check 1
    template <typename Left, typename Right>
    inline predicate_result
    call_impl( Left const& left, Right const& right, mpl::true_ ) const
    {
        return (*this)( right, &left[0] );
    }

    template <typename Left, typename Right>
    inline predicate_result
    operator()( Left const& left, Right const& right ) const
    {
        typedef typename is_array<Left>::type left_is_array;
        return call_impl( left, right, left_is_array() );
    }
};

首先,BOOST_CHECK_EQUAL在编译时决定参数是否为数组。在检查1中它们是,并且数组降级为指针。然后它决定如何比较参数。如果参数的类型为char *,则将它们作为C字符串进行比较。否则,它使用operator ==。所以问题是C类不是char *,所以检查2是使用operator ==完成的。但是C类没有operator ==,所以编译器决定隐式地将c1和c2转换为char *,此时定义了operator ==,但是将它们作为地址而不是C字符串进行比较。

所以我们最终遇到了一个相当奇怪的情况:Boost Test总是将char *参数比作C字符串,但它并不知道比较c1和c2的唯一方法是将它们转换为char *。

我的问题是,我们怎么能在这里做得更好?例如,有没有办法在编译时理解当为c1和c2调用operator ==时它将使用它们隐式转换为char *?这有点像使用decltype()在编译时计算表达式的返回类型,除了我们需要弄清楚表达式的参数类型(即c1 == c2)。

1 个答案:

答案 0 :(得分:1)

我认为你不能真正支持这种情况,因为我能想到的所有SFINAE技术都会遇到模糊的重载。

事实上,这正是Boost has_equal_to<A,B,R>类型特征所记录的限制:

  

如果运算符仅存在于类型A且B可转换为A,则存在问题。在这种情况下,编译器将报告模糊的重载。