std :: is_same <t,t> :: value总是为真吗?

Tre*_*key 7 c++ templates static-assert type-traits c++11

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

///
/// 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");
}
Run Code Online (Sandbox Code Playgroud)

!std::is_same<value_type, value_type>::value 看起来过于冗长.

我应该将这些陈述改为static_assert(false,"...")吗?

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

std::is_same<t,t>::value永远是真的吗?

Yak*_*ont 9

您发布的代码生成错误,无需诊断.

替换它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 "";
}
Run Code Online (Sandbox Code Playgroud)

主模板to_string无法使用任何类型进行实例化.C++标准要求所有模板(包括主模板)必须具有有效的实例化(在标准中称为有效的特化).(还有其他要求,例如,如果涉及包,至少有一个这样的实例化必须具有非空包装等).

你可能会抱怨"它编译和工作",但这不是诊断所需的手段.C++标准对编译器在遇到"格式错误的无需诊断"的情况下所做的事情设置了限制.它无法检测到它,并轻率地编译"工作".它可以假设它是不可能的,并且如果它确实发生则生成格式错误的代码.它可以尝试检测它,失败并执行上述任一操作.它可以尝试检测它,成功并生成错误消息.它可以检测到它,成功并生成代码,用于将您在浏览器中查看过的每个图像的缩略图通过电子邮件发送给所有联系人.

它是不正确的,不需要诊断.

我会自己避免这样的代码.

现在,有人可能会争辩说有人可以专门is_same<T,T>回归false,但这也会使你的程序形成非法的模板专业化,std这违反了标准中所写的模板要求.

更换!std::is_same<value_type, value_type>::valuefalse就干脆允许你的编译器实现是病态形成你的代码,并生成一个错误消息.从某种意义上说,这是一件好事,因为形成错误的代码将来可能以任意方式破坏.

哈克修复它的方法

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

template<class T, class U>
struct my_is_same:std::is_same<T,U> {};
Run Code Online (Sandbox Code Playgroud)

承认专业化漏洞的可能性.这仍然是代码味道.

正确的方法来解决它

写这两者的正确方法需要一些工作.

首先,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);
  }
}
Run Code Online (Sandbox Code Playgroud)

目标是最终用户只是做一个utility::from_string<Bob>(b)utility::to_string(bob)它的工作.

基础的反弹到标签调度.要进行自定义,您需要重载tag-dispatch版本.

要实现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 );
Run Code Online (Sandbox Code Playgroud)

请注意,它们不是模板的模板或专业化.

要处理类型std或内置类型,只需在中定义类似的重载namespace utility.

现在,ADL和标签调度将带您到正确的字符串函数.无需更改名称空间以定义到/来自字符串.

如果您调用to_stringfrom_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;
}
Run Code Online (Sandbox Code Playgroud)

实例.

(使用friend不是必需的,函数只需要在同一名称空间中Bob或内部namespace utility).

  • @Rumburak"如果没有为模板生成有效的专业化,并且该模板未实例化,则模板格式错误,无需诊断." §14.6/ 8(N3797); 它应该是Yakk答案中的"主要模板",但不是"主要专业化". (2认同)