你发布的代码格式不正确,不需要进行诊断。
用
static_assert(false, ...)
替换它可以使编译器“注意到你的代码格式不正确”。在此之前,代码已经存在格式错误,只是编译器没有注意到。
我有两种解决方法。其中一种是黑客方法,但合法。另一种更干净,但需要你编写更多的代码。
本回答的第一部分是说明你的代码为什么格式不正确。接下来的两个部分是解决方案。
代码为什么格式不正确?
template <typename value_type>
std::string to_string(const value_type &value)
{
static_assert(!std::is_same<value_type, value_type>::value, "Unspecialized usage of to_string not supported");
return "";
}
to_string
的主要模板无法实例化任何类型。C ++标准要求所有模板,包括主模板,在实例化时必须具有有效的特化(在标准术语中称为有效特化)。 (还有其他要求,例如如果涉及包,则至少一个此类实例化必须具有非空包等)。
您可能会抱怨“它已编译并工作”,但这是“不需要诊断”的意思。 C ++标准对编译器在遇到“不良形式无需诊断”情况时所做的任何事情都没有任何限制。 它可能无法检测到它并轻松编译出“工作”。 它可以假定它是不可能的,并在发生时生成格式错误的代码。 它可以尝试检测它,失败,并执行上述任何一项操作。 它可以尝试检测它,成功,并生成错误消息。 它可以检测到它,成功并生成代码,该代码向所有联系人发送您在浏览器中查看的每个图像的缩略图。
它是不良形式的,不需要诊断。
我会避免这样的代码。
现在,有人可能会争辩说,某个人可能会将
is_same<T,T>
专门化为返回
false
,但这也会使您的程序不良形式化,因为它是从
std
中的模板非法特化违反了标准中编写的模板的要求。
用
false
替换
!std :: is_same<value_type,value_type> :: value
只会允许编译器意识到您的代码是不良形式的,并生成错误消息。 在某种意义上,这是一件好事,因为不良形式的代码将来可能会以任意方式中断。
修复它的愚蠢方法是创建一个
template<class T, class U>
struct my_is_same:std::is_same<T,U> {}
这段代码存在特化漏洞的可能,这是一种代码异味。
正确的修复方式
正确的编写方式需要一些工作。
首先,使用标签分派(tag dispatching)而不是模板特化(template specialization)来创建基于to_string
和from_string
的方法:
namespace utility {
template<class T>struct tag_t {};
template <typename value_type>
std::string to_string(tag_t<value_type>, const value_type &value) = delete;
template <typename value_type>
std::string to_string(const value_type &value) {
return to_string(tag_t<value_type>{}, value);
}
template <typename return_type>
return_type from_string(tag_t<return_type>, const std::string &source) = delete;
template <typename return_type>
return_type from_string(const std::string &source) {
return from_string(tag_t<return_type>{}, source);
}
}
目标是让最终用户只需执行utility::from_string<Bob>(b)
或utility::to_string(bob)
即可完成操作。
基本函数会跳转到tag-dispatches。要自定义,您需要重载版本。
要实现to/from字符串,在Bob
的命名空间中编写这两个函数:
Bob from_string( utility::tag_t<Bob>, const std::string& source );
std::string to_string( utility::tag_t<Bob>, const Bob& source );
请注意,它们不是模板或模板的特化。
要处理std
或内置类型中的类型,只需在namespace utility
中定义类似的重载即可。
现在,ADL和标签分派会将您带到正确的to/from字符串函数。无需更改命名空间来定义to/from字符串。
如果您调用to_string
或from_string
而没有有效的tag_t
重载,则最终会调用=delete
并获得“未找到重载”错误。
测试代码:
struct Bob {
friend std::string to_string( utility::tag_t<Bob>, Bob const& ) { return "bob"; }
friend Bob from_string( utility::tag_t<Bob>, std::string const&s ) { if (s=="bob") return {}; exit(-1); }
};
int main() {
Bob b = utility::from_string<Bob>("bob");
std::cout << "Bob is " << utility::to_string(b) << "\n";
b = utility::from_string<Bob>( utility::to_string(b) );
std::cout << "Bob is " << utility::to_string(b) << std::endl;
Bob b2 = utility::from_string<Bob>("not bob");
std::cout << "This line never runs\n";
(void)b2;
}
现场示例。
(使用friend
并非必需,该函数只需要与Bob
在同一名称空间中或在namespace utility
中即可。)
template<class T, class U> struct is_same : std::false_type {}; template<class T> struct is_same<T, T> : std::true_type {};
所以我倾向于回答是的。 - clctostatic_assert(false, "");
来替换那些代码吗?(提示:你的代码将无法编译) - Praetorian