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

7

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

///
/// 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是否总是为真?


2
cppreference 上说这是一种可能的实现方式 template<class T, class U> struct is_same : std::false_type {}; template<class T> struct is_same<T, T> : std::true_type {}; 所以我倾向于回答是的。 - clcto
标准的表44。第一个。 - DeiDei
3
你尝试过用 static_assert(false, ""); 来替换那些代码吗?(提示:你的代码将无法编译) - Praetorian
1个回答

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 "";
}
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_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)即可完成操作。

基本函数会跳转到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_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;
}

现场示例

(使用friend并非必需,该函数只需要与Bob在同一名称空间中或在namespace utility中即可。)


标准要求“所有专业化,包括主专业化,都必须具有有效的实例化”,请问这个要求在哪里规定? - Rumburak
2
如果模板没有有效的特例化可以被生成,而且该模板没有实例化,则该模板是非法的,无需诊断。 §14.6/8(N3797); Yakk的答案应该是“主模板”,而不是“主特例化”。 - Arne Vogel
@arne 谢谢 -- 这个周末我没有时间用真正的电脑,而且在手机上搜索标准很麻烦。 - Yakk - Adam Nevraumont
1
@ArneVogel 谢谢!顺便说一句,我实际上更喜欢解决方案1,但是带有类似template<typename T> struct wrong { static constexpr auto value = false; };的内容。 它清晰地显示了意图,并且比标签调度变体短得多。 - Rumburak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接