编译时检测缺少用户定义的 to_string()
Compile-time detection of missing user-defined to_string()
我想为我创建的每个对象类型提供一个 to_string(obj)
函数。
我找到了 , applied the ,并且有效。到目前为止一切顺利。
然后我创建了一个新类型,但忘了为它写一个 to_string()
(或者更好:我不小心让它无法被 ADL 访问)。问题是:我的程序仍然 编译得很好 ,并且在运行时我得到一个模糊的堆栈溢出 (TM)。
有没有办法获取合理的错误消息?
这里有一个小程序来演示问题:notstd::to_string()
和notstd::adl_helper::as_string()
之间的无限递归。
#include <iostream>
#include <string>
namespace notstd {
namespace adl_helper {
using std::to_string;
template<class T>
std::string as_string( T&& t ) {
return to_string( std::forward<T>(t) );
}
}
template<class T>
std::string to_string( T&& t ) {
std::cout << "called" << std::endl; // <-- this is to show what's going on
return adl_helper::as_string(std::forward<T>(t));
}
class A {
/* both versions are needed, or the perfect forwarding candidate will
* always be chosen by the compiler in case of a non-perfect match */
//friend std::string to_string(A &a) { return std::string("a"); }
//friend std::string to_string(const A &a) { return std::string("a"); }
};
}
int main(int argc, char** argv) {
notstd::A a;
std::cout << to_string(a) << std::endl;
}
我尝试创建一个包装函数,它接受另一个参数,用于执行反递归检查,如下所示:
#include <iostream>
#include <string>
#include <cassert>
namespace notstd {
namespace wrap_std {
std::string to_string(double v, bool) { return std::to_string(v); }
/* .... etc..... */
}
namespace adl_helper {
using wrap_std::to_string;
template<class T>
std::string as_string( T&& t ) {
return to_string( std::forward<T>(t), true );
}
}
template<class T>
std::string to_string( T&& t, bool recurring = false ) {
std::cout << "called" << std::endl;
assert(!recurring);
return adl_helper::as_string(std::forward<T>(t));
}
class A {
/* both versions are needed, or the perfect forwarding candidate will
* always be chosen by the compiler in case of a non-perfect match */
//friend std::string to_string(A &a) { return std::string("A"); }
//friend std::string to_string(const A &a) { return std::string("A"); }
};
}
int main(int argc, char** argv) {
notstd::A a;
std::cout << to_string(a) << std::endl;
}
这里的问题是:
- 我必须包装 所有 std::to_string() 重载
- 我只会得到一个运行时错误,但我觉得这个问题可以而且应该在编译时检测到
- 我可能会增加一些开销,因为一些东西只在开发期间有用:也许我可以添加一些宏来在发布模式下停用所有这些,但它会增加更多的工作
也许我可以使用模板来包装 std::to_string()
并为我的类型创建特化...这将是一个完全不同的野兽,但至少它会提供一个编译时错误,如果合适的特化是无法使用。我会再次包装所有 std::to_string()
重载,并且我可能不得不(几乎)忘记 ADL,至少在所有编译器支持 c++20 之前,如果我理解得很好的话。
有没有人有更好的解决方案?
谢谢!
accepted 的想法是不同的:你把 A
放在 notstd
命名空间之外,然后使用合格的 notstd::to_string
而不是不合格的 to_string
。即:
namespace notstd {
// ...
}
class A {
friend std::string to_string(const A&);
};
A a;
std::cout << notstd::to_string(a);
现在,如果没有 friend
函数,您的代码将无法编译。此外,您只需要一个友元函数(采用 const A&
),因为 notstd::to_string(T&&)
不会出现在 adl_helper::as_string(T&&)
.
内的重载集中
把 A
放在 notstd
里面搞砸了。你有无限递归问题,你需要两个朋友来处理 A
和 const A
情况下 notstd::to_string(T&&)
候选人:如果只定义了一个朋友,那么候选人是更好的匹配其中一种情况是因为 const
限定符应该是 added/dropped 来调用友元函数。
我想为我创建的每个对象类型提供一个 to_string(obj)
函数。
我找到了
然后我创建了一个新类型,但忘了为它写一个 to_string()
(或者更好:我不小心让它无法被 ADL 访问)。问题是:我的程序仍然 编译得很好 ,并且在运行时我得到一个模糊的堆栈溢出 (TM)。
有没有办法获取合理的错误消息?
这里有一个小程序来演示问题:notstd::to_string()
和notstd::adl_helper::as_string()
之间的无限递归。
#include <iostream>
#include <string>
namespace notstd {
namespace adl_helper {
using std::to_string;
template<class T>
std::string as_string( T&& t ) {
return to_string( std::forward<T>(t) );
}
}
template<class T>
std::string to_string( T&& t ) {
std::cout << "called" << std::endl; // <-- this is to show what's going on
return adl_helper::as_string(std::forward<T>(t));
}
class A {
/* both versions are needed, or the perfect forwarding candidate will
* always be chosen by the compiler in case of a non-perfect match */
//friend std::string to_string(A &a) { return std::string("a"); }
//friend std::string to_string(const A &a) { return std::string("a"); }
};
}
int main(int argc, char** argv) {
notstd::A a;
std::cout << to_string(a) << std::endl;
}
我尝试创建一个包装函数,它接受另一个参数,用于执行反递归检查,如下所示:
#include <iostream>
#include <string>
#include <cassert>
namespace notstd {
namespace wrap_std {
std::string to_string(double v, bool) { return std::to_string(v); }
/* .... etc..... */
}
namespace adl_helper {
using wrap_std::to_string;
template<class T>
std::string as_string( T&& t ) {
return to_string( std::forward<T>(t), true );
}
}
template<class T>
std::string to_string( T&& t, bool recurring = false ) {
std::cout << "called" << std::endl;
assert(!recurring);
return adl_helper::as_string(std::forward<T>(t));
}
class A {
/* both versions are needed, or the perfect forwarding candidate will
* always be chosen by the compiler in case of a non-perfect match */
//friend std::string to_string(A &a) { return std::string("A"); }
//friend std::string to_string(const A &a) { return std::string("A"); }
};
}
int main(int argc, char** argv) {
notstd::A a;
std::cout << to_string(a) << std::endl;
}
这里的问题是:
- 我必须包装 所有 std::to_string() 重载
- 我只会得到一个运行时错误,但我觉得这个问题可以而且应该在编译时检测到
- 我可能会增加一些开销,因为一些东西只在开发期间有用:也许我可以添加一些宏来在发布模式下停用所有这些,但它会增加更多的工作
也许我可以使用模板来包装 std::to_string()
并为我的类型创建特化...这将是一个完全不同的野兽,但至少它会提供一个编译时错误,如果合适的特化是无法使用。我会再次包装所有 std::to_string()
重载,并且我可能不得不(几乎)忘记 ADL,至少在所有编译器支持 c++20 之前,如果我理解得很好的话。
有没有人有更好的解决方案?
谢谢!
accepted A
放在 notstd
命名空间之外,然后使用合格的 notstd::to_string
而不是不合格的 to_string
。即:
namespace notstd {
// ...
}
class A {
friend std::string to_string(const A&);
};
A a;
std::cout << notstd::to_string(a);
现在,如果没有 friend
函数,您的代码将无法编译。此外,您只需要一个友元函数(采用 const A&
),因为 notstd::to_string(T&&)
不会出现在 adl_helper::as_string(T&&)
.
把 A
放在 notstd
里面搞砸了。你有无限递归问题,你需要两个朋友来处理 A
和 const A
情况下 notstd::to_string(T&&)
候选人:如果只定义了一个朋友,那么候选人是更好的匹配其中一种情况是因为 const
限定符应该是 added/dropped 来调用友元函数。