std :: is_same< t,t> :: value始终为真吗? [英] Is std::is_same<t,t>::value always true?

查看:79
本文介绍了std :: is_same< t,t> :: value始终为真吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  /// 
///函数,用于将用户定义的对象转换为字符串值
///
template< typename value_type>
std :: string to_string(const value_type& value)
{
static_assert(!std :: is_same< value_type,value_type> :: value,不支持to_string的非特殊用法) ;
返回;
}

///
///一个特殊功能,用于将用户定义的对象从字符串转换为值
///
模板< typename return_type>
return_type from_string(const std :: string& source)
{
static_assert(!std :: is_same< return_type,return_type> :: value,不支持from_string的非特殊用法) ;
}

!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,"不支持to_string的非专业用法 );
返回;
}

to_string 的主要模板不能为用任何类型实例化。 C ++标准要求所有模板(包括主模板)必须具有有效的实例化(在标准术语中称为有效的专业化)。 (还有其他要求,例如至少有一个这样的实例化,如果涉及到包,则必须具有非空包,等等)。


您可能会抱怨它已编译并工作。 ,但这是不需要诊断的意思。当编译器遇到格式错误,不需要诊断时,C ++标准将约束放在编译器执行的操作上。案件。它可能无法检测到它并巧妙地编译起作用。可以假设这是不可能的,如果确实发生,则生成格式错误的代码。它可以尝试检测到它,失败并执行以上任一操作。它可以尝试检测它,成功并生成错误消息。它可以检测到并成功,并生成代码,将过去一年在浏览器中查看的每张图像的缩略图通过电子邮件发送给所有联系人。


它的格式不正确,并且不需要诊断。


我自己会避免使用此类代码。


现在,有人可能会争辩说某人可以在某个地方专门研究 is_same< T,T> 返回 false ,但这也会使您的程序不适当地构成为<$ c的模板非法专门化形式$ c> std 违反了标准中对模板的要求。


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


缺乏修复方法


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

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

承认存在专业漏洞的可能性。这仍然是代码的味道。


正确的解决方法


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


首先,基于标签分配 to_string from_string 代替模板专门化

 命名空间实用程序{
template< class T> struct tag_t {};
模板< typename value_type>
std :: string to_string(tag_t< value_type> ;, const value_type& value)=删除;
模板< typename value_type>
std :: string to_string(const value_type& value){
return to_string(tag_t< value_type> {},value);
}

模板< typename return_type>
return_type from_string(tag_t< return_type> ;, const std :: string& source)=删除;
模板< typename return_type>
return_type from_string(const std :: string& source){
return from_string(tag_t< return_type> {},源);
}
}

目标是最终用户只需执行 utility :: from_string< Bob>(b) utility :: to_string(bob)即可。


基本的反弹到标记调度。要进行自定义,请重载 tag-dispatch 版本。


要在 Bob 的命名空间中实现to / from字符串, code>编写这两个函数:

  Bob from_string(utility :: tag_t< Bob> ;, const std :: string& source); 
std :: string to_string(Utility :: tag_t< Bob> ;, const Bob& source);

注意它们不是模板或模板的特殊化。


std 或内置类型,只需在 namespace实用程序中定义类似的重载。


<现在,ADL和标签分派将您带到正确的to / from字符串函数。


如果调用 to_string from_string code>没有有效的 tag_t 重载,您最终会调用 = delete 并得到一个重载找不到错误。


测试代码:

  struct Bob {
std :: string to_string(实用程序:: tag_t< Bob,Bob const&){返回 bob; }
朋友Bob from_string(Utility :: tag_t< Bob> ;, std :: string const& s){if(s == bob)return {};退出(-1); }
};

int main(){
Bob b = Utility :: from_string< Bob>( bob);
std :: cout<< 鲍勃是 <<实用程序:: to_string(b)<< \n;
b =实用程序:: from_string< Bob>(实用程序:: to_string(b));
std :: cout<< 鲍勃是 <<实用程序:: to_string(b)<< std :: endl;
Bob b2 = utility :: from_string< Bob>( not bob);
std :: cout<< 此行永远不会运行;
(void)b2;
}

实时示例


(不需要使用 friend ,该功能只是必须与 Bob 在同一命名空间中,或在 namespace实用程序中)。


I've inherited some code that looks like this:

///
/// 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 seems overly verbose.

Should I change these statements to static_assert(false,"...")?

I'm not sure if it was expressed this way to handle some kind of edge case, or if false is indeed equivalent.

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

解决方案

The code you posted is ill formed with no diagnostic required.

Replacing it with static_assert(false, ...) makes the compiler notice the fact your code is ill-formed. The code was ill-formed before, the compiler just didn't notice it.

I have two fixes to your problem. One is a hack, but legal. The other is much cleaner, but requires you to write more code.

The first section of this answer is why your code is ill-formed. The next two are solutions.

Why is the code ill-formed?

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 "";
}

The primary template of to_string cannot be instantiated with any type. The C++ standard demands that all templates, including primary templates, must have a valid instantiation (which in standardese is called a valid specialization). (There are other requirements, like at least one such instantiation must have an non-empty pack if there are packs involved, etc).

You may complain that "it compiled and worked", but that is no diagnostic required means. The C++ standard places zero constraints on what the compiler does when it runs into a "ill-formed no diagnostic required" case. It can fail to detect it and blithely compile that "works". It can assume it is impossible, and generate malformed code if it does happen. It can attempt to detect it, fail, and do either of the above. It can attempt to detect it, succeed, and generate an error message. It can detect it, succeed, and generate code that emails thumbnails of every image you looked at in your browser over the last year to all of your contacts.

It is ill-formed, and no diagnostic is required.

I would avoid such code myself.

Now, one might argue that someone could somewhere specialize is_same<T,T> to return false, but that would also make your program ill formed as an illegal specialization of a template from std that violates the requirements on the template as written in the standard.

Replacing !std::is_same<value_type, value_type>::value with false will simply permit your compiler to realize your code is ill formed, and generate an error message. This is, in a sense, a good thing, as ill formed code can break in arbitrary ways in the future.

Hack way to fix it

The stupid way to fix this is to create

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

which admits the possibility of the specialization loophole. This is still code smell.

Right way to fix it

The right way to write both of these requires a bit of work.

First, to_string and from_string based off tag dispatching instead of template specialization:

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);
  }
}

The goal is that the end user simply does a utility::from_string<Bob>(b) or utility::to_string(bob) and it works.

The base ones bounce to tag-dispatches. To customize, you overload the tag-dispatch versions.

To implement the to/from string, in the namespace of Bob write these two functions:

Bob from_string( utility::tag_t<Bob>, const std::string& source );
std::string to_string( utility::tag_t<Bob>, const Bob& source );

notice they are not templates or specializations of templates.

To handle types in std or built-in types, simply define similar overloads in namespace utility.

Now, ADL and tag dispatching take you to the correct to/from string function. No need to change namespaces to define to/from string.

If you call to_string or from_string without a valid tag_t overload, you end up calling the =delete and getting an "overload not found" error.

Test code:

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;
}

Live example.

(Use of friend is not required, the function just has to be in the same namespace as Bob or within namespace utility).

这篇关于std :: is_same&lt; t,t&gt; :: value始终为真吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆