为什么我不能将继承的错误用作SFINAE? [英] Why I can't use inheritance's error as SFINAE?

查看:62
本文介绍了为什么我不能将继承的错误用作SFINAE?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码,但无法编译:

I have this code, but it does not compile:

#include <iostream>
#include <stdexcept>
#include <cassert>


template< class > struct id{};
template< class U, class V> struct base: public id<U>, public id<V>
{
    static const bool value = true;
};

template< class U, class V>
struct is_different
{

  typedef char (&true_)[1];
  typedef char (&false_)[2];

  template< class T, class K, bool  = base<T,K>::value >
  struct checker;



  template< class T, class K>
  static true_  test( checker<T,K>* );

  template< class , class >
  static false_  test(...);


  static const bool value = sizeof( test<U,V>(0) ) == sizeof(true_);

};


int main (void) 
{
    bool b1 = is_different<int,float>::value;
    bool b2 = is_different<int,int>::value; // <--- error

    std::cout << std::boolalpha << b1 << '\n'
                                << b2  << '\n';
    return 0;       
}

错误:

main.cpp: In instantiation of ‘struct base<int, int>’:
main.cpp:25:17:   required by substitution of ‘template<class T, class K> static char (& is_different<U, V>::test(is_different<U, V>::checker<T, K>*))[1] [with T = T; K = K; U = int; V = int] [with T = int; K = int]’
main.cpp:31:41:   required from ‘const bool is_different<int, int>::value’
main.cpp:39:38:   required from here
main.cpp:7:36: error: duplicate base type ‘id<int>’ invalid
 template< class U, class V> struct base: public id<U>, public id<V>

为什么不能将重复继承失败用作SFINAE?

Why I can't use duplicate inheritance's failure as SFINAE?

推荐答案

SFINAE仅适用于直接在函数签名内的事物.您得到的编译错误发生在base<T, K>的实例化中,这是从函数签名中删除的两个步骤.

SFINAE only applies to things directly within the function signature. The compilation error you get happens within the instantiation of base<T, K>, which is two steps removed from the function signature.

那是为什么,你为什么要这么努力?

That said, why do you do this the hard way?

template <typename T, typename U>
struct is_same { static const bool value = false; };

template <typename T>
struct is_same<T, T> { static const bool value = true; };

template <typename T, typename U>
struct is_different { static const bool value = !is_same<T, U>::value; };

因此,为了在评论中回答您的问题,让我解释一下有关SFINAE的更多信息.该标准在17.8.2p8中指定了SFINAE:

So, to answer your question in the comment, let me explain a bit more about SFINAE. The standard specifies SFINAE in 17.8.2p8:

如果替换导致无效的类型或表达式,则类型推导失败.无效的类型或表达式是一种使用替换参数编写的格式或格式.只有在函数类型及其模板参数类型的直接上下文中的无效类型和表达式才可能导致推论失败. [注意:对替换类型和表达式的求值可能会导致副作用,例如实例化类模板专业化和/或函数模板专业化,生成隐式定义的函数等.此类副作用不在即时上下文"中,并可能导致程序格式错误. -尾注]

If a substitution results in an invalid type or rexpression, type deduction fails. An invalid type or rexpression is one that would be ill-formed if written using the substituted arguments. Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of the class template specializations and/or function template specializations, the generation of implicitly-defined functions, et. Such side effects are not in the "immediate context" and can result in the program being ill-formed. - end note]

这里有问题的是即时上下文".您在这里要做的两件事是从即时上下文中删除错误.

The thing that's problematic here is the "immediate context". You are doing two things here that remove your error from the immediate context.

  • 在评估checker的默认参数表达式期间发生错误.尽管此评估是由test的参数列表中出现checker<T,K>触发的,该实例被实例化以进行重载解析,但这不是直接的上下文.您可以通过将base的定义更改为template <typename T, typename U> struct base {};来进行尝试.这样可以使base实例化中的错误消失,在默认参数表达式的上下文中将其替换为错误(没有名为value的成员").但是SFINAE仍然不会触发.您仍然会遇到严重错误.
  • 在实例化base的定义时发生错误.这是从重载解决方案中删除的又一个步骤.出于同样的原因,将成员放入类型为T::foobarbase中对于查找T是否包含类型foobar无效:base的定义不再是SFINAE上下文.您可以通过从图片中删除checker来单独查看这是一个错误,例如这样做:
  • The error occurs during evaluation of the default argument expression of checker. Although this evaluation is triggered by the occurrence of checker<T,K> in the argument list of test, which gets instantiated for overload resolution, it isn't the immediate context. You can try this out by changing the definition of base to template <typename T, typename U> struct base {};. This makes the error in the instantiation of base go away, replacing it with an error in the context of the default argument expression ("does not have a member called value"). But SFINAE will still not trigger; you'll still get a hard error.
  • The error occurs in the instantiation of the definition of base. This is yet another step removed from overload resolution. For the same reason, putting a member into base with type T::foobar doesn't work for finding out whether T contains a type foobar: the definition of base is not a SFINAE context anymore. You can see that this alone is an error by removing checker from the picture, e.g. doing this:

 

template <typename U, typename V> struct base: public id<U>, public id<V> {
    typedef void type;
};
template <typename U, typename V> struct is_different {
  struct true_ { char c[1]; };  // I don't like sizeof(reference type),
  struct false_ { char c[2]; }; // makes me nervous.
  template <typename T, typename K>
  static true_ test(typename base<T, K>::type*);
  template <typename, typename>
  static false_ test(...);

  static const bool value = sizeof(test<U, V>(0)) == sizeof(true_);
};

现在base实例化不会隐藏在模板默认参数表达式的后面,而是位于签名中,但是您仍然会收到错误消息.

Now the base instantiation doesn't hide behind a template default argument expression, but is right there in the signature, but you'll still get an error.

这篇关于为什么我不能将继承的错误用作SFINAE?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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