std::is_same<t,t>::value总是正确的吗?

Is std::is_same<t,t>::value always true?

本文关键字:is same lt gt std value      更新时间:2023-10-16

>我继承了一些看起来像这样的代码:

///
/// A specializable function for converting a user-defined object to a string value
///
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 "";
}
///
/// A specializable function for converting a user-defined object from a string to a value
///
template <typename return_type>
return_type from_string(const std::string &source)
{
    static_assert(!std::is_same<return_type, return_type>::value, "Unspecialized usage of from_string not supported");
}

!std::is_same<value_type, value_type>::value似乎过于冗长。

我应该将这些语句更改为static_assert(false,"...")吗?

我不确定它是否以这种方式表达以处理某种边缘情况,或者false是否确实是等效的。

std::is_same<t,t>::value总是正确的吗?

您发布的代码格式不正确,无需诊断。

将其替换为 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违反了标准中编写的模板要求。

!std::is_same<value_type, value_type>::value替换为 false 只会让您的编译器意识到您的代码格式不正确,并生成错误消息。 从某种意义上说,这是一件好事,因为格式错误的代码将来可能会以任意方式中断。

破解修复它的方法

解决此问题的愚蠢方法是创建

template<class T, class U>
struct my_is_same:std::is_same<T,U> {};

这承认了专业化漏洞的可能性。 这仍然是代码气味。

正确的解决方法

编写这两个的正确方法需要一些工作。

首先,基于代码调度而不是模板专用化的to_stringfrom_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)即可正常工作。

基本反弹到标签调度。 要进行自定义,您需要重载标记调度版本。

若要实现 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 字符串。

如果在没有有效tag_t重载的情况下调用to_stringfrom_string,则最终会调用=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 runsn";
    (void)b2;
}

活生生的例子。

(不需要使用 friend,该函数只需与 Bob 位于同一命名空间中或在 namespace utility 内(。