这是一个使用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
)。
答案 0 :(得分:1)
我认为你不能真正支持这种情况,因为我能想到的所有SFINAE技术都会遇到模糊的重载。
事实上,这正是Boost has_equal_to<A,B,R>
类型特征所记录的限制:
如果运算符仅存在于类型A且B可转换为A,则存在问题。在这种情况下,编译器将报告模糊的重载。