Boost Test Boost _CHECK_EQUAL,类型可转换为数组



下面是一个使用Boost Test的简单程序,它的行为"奇怪":

#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 Test中的这段代码(我使用的是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字符串进行比较。否则,它将使用运算符==。所以问题是类C不是char*,所以检查2是使用运算符==完成的。但是C类没有运算符==,所以编译器决定隐式地将c1和c2转换为char*,在这一点上定义了运算符==但将它们作为地址而不是C字符串进行比较。

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

我的问题是,我们如何才能在这里做得更好?例如,有没有一种方法可以在编译时理解,当为c1和c2调用运算符==时,它将使用它们到char*的隐式转换?这有点像在编译时使用decltype()来计算表达式的返回类型,只是我们需要计算表达式的参数类型(即c1 == c2)。

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

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

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

最新更新