基于对函数的参数调用流输出运算符的能力重载函数
Overloading a function based on the ability to call the stream output operator on its arguments
我有一个泛型函数,它接受两个参数,比较它们,如果它们不相等,则打印一条消息。现在,我只有这个相对愚蠢的功能:
template <typename T>
static void AreEqual(const T& expected,
const T& actual,
const std::string& message = "") {
if (!(actual == expected)) {
std::cout << message;
}
}
多年来,这已经充分发挥作用。它最常与原语一起调用,但也用于比较较大的用户定义结构/类。
我想通过提供一个重载来扩展函数,当它们不匹配时打印预期值和实际值,但不破坏定义operator==
但不定义operator<<
的类的函数。我的想法是创建一个重载,如果缺少重载,则使用 SFINAE 禁用重载operator<<
。到目前为止,我已经想出了这个:
template <
typename T,
typename = typename std::enable_if_t<
std::is_same_v<decltype(std::cout << *((T*)nullptr)), decltype(std::cout)>>>
static void AreEqual(const T& expected,
const T& actual,
const std::string& message = "") {
if (!(actual == expected)) {
std::cout << "Expected " << expected << ", got " << actual << ". " << message;
}
}
这编译了,但它没有被选中T
int
或std::string
,我不确定为什么。我的第一个怀疑是我对is_same_v
的论点以某种方式是畸形的,但如果是这样的话,我不知道如何或如何弄清楚如何解决它。
问题1:所有这些甚至有必要吗?我可以在没有模板元编程的情况下获得相同的结果吗(最好在坚持使用 C++11 的同时)
问题 2:如果这是最好的方法,如何有效地调试我的模板?
你可以做这样的事情:
struct overload_low_priority {};
struct overload_high_priority : overload_low_priority {};
template <typename T>
static auto AreEqualImpl(const T& expected,
const T& actual,
const std::string& message,
overload_high_priority)
-> decltype(std::cout << expected, void()) // SFINAE
{
if (!(actual == expected)) {
std::cout << "Expected " << expected << ", got " << actual << ". " << message;
}
}
template <typename T>
static void AreEqualImpl(const T& expected,
const T& actual,
const std::string& message,
overload_low_priority) // Fallback
{
if (!(actual == expected)) {
std::cout << message;
}
}
template <typename T>
static void AreEqual(const T& expected,
const T& actual,
const std::string& message = "")
{
AreEqualImpl(expected, actual, message, overload_high_priority{});
}
我提出了一些不同的东西。
而不是std::cout
(或不expected
)值和actual
,你可以打印从值调用的函数返回的值。因此,您可以区分(重载)被调用函数的行为。
我的意思是。。。假设您编写了两个版本的函数maybePrint()
。
第一个是模板直通函数,仅当模板类型可打印时才启用 SFINAE
template <typename T>
auto maybePrint (T const & t) -> decltype( std::cout << t, t )
{ return t; }
第二个,当第一个不可用时调用(所以当参数不可打印时),返回一个信息丰富的字符串(嗯......也许选择一个更好的字符串)[编辑:根据Jarod42的报告修改]
template <typename ... Ts>
std::string maybePrint (Ts const & ...)
{ return "[maybe not]"; }
所以你的AreEqual()
变成
template <typename T>
static void AreEqual(const T& expected,
const T& actual,
const std::string& message = "")
{
if ( ! (actual == expected) )
std::cout << "Expected " << maybePrint(expected) << ", got "
<< maybePrint(actual) << ". " << message;
}
我建议此解决方案还因为,明天或遥远的将来,您可能会想修改AreEqual()
以区分模板类型。
这是因为以下调用
AreEqual(1, 2l, "abcn");
给出编译错误,因为编译器无法在T = int
(1
int
)和T = long
(2l
long
)之间进行选择。
如果您重写AreEqual()
收到(可能)两种不同类型的两个参数
template <typename T1, typename T2>
static void AreEqual (T1 const & expected,
T2 const & actual,
std::string const & message = "")
{
if ( ! (actual == expected) )
std::cout << "Expected " << maybePrint(expected) << ", got "
<< maybePrint(actual) << ". " << message;
}
前面的调用编译是因为T1
int
推导,T2
推导long
。
如果您根据T1
启用AreEqual()
的一个版本或另一个版本T2
则(可能)有四种情况(T1
和T2
可打印的;T1
可打印的,T2
不是;T2
可打印的,T1
不是;T1
和T2
不可打印)所以四个版本的AreEqual()
.
通过maybePrint()
您可以维护单个AreEqual()
。
- 如何从void函数输出字符串
- 简化C++包括变量名称和函数输出的模式
- 素数检查 C++ 函数输出非素数的数字
- 在C++目录中搜索文件的函数输出中出错
- 用于计算周长和面积的程序,而不是从 void 函数输出值
- 使用 if/else if 的函数输出问题
- (c++) 返回和函数输出之间的错误
- 使用函数输出最高平均值
- 为什么另一个函数输出错误的值
- C++函数输出泛型结构
- 在 c++11 中,是否仍然需要传入对接受函数输出的对象的引用
- 虚拟函数输出奇怪的值
- 将打印到控制台的函数输出重定向到字符串
- c++ 将函数输出重定向到编译的程序输入,反之亦然
- 未修改 CUDA 内核函数输出变量
- iomanip意外影响ctime函数输出
- 素数分解函数输出
- 继承构造函数输出
- 多维数组函数输出垃圾
- 递归函数-输出不正确