访问未定义子类型时的自定义编译错误消息 [英] Custom compile error message when undefined subtype is accessed

查看:83
本文介绍了访问未定义子类型时的自定义编译错误消息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些类型具有相同名称的子类型:

I have some types which have sub-types with the same name each:

struct TypeA {
    typedef int subtype;
};
struct TypeB {
    typedef float subtype;
};

以及没有此子类型但在相同上下文中使用的类型:

and also types which don't have this sub-type but which are used in the same context:

struct TypeC {
    // (no subtype defined)
};

如何添加提供自定义编译错误消息的虚拟子类型?

How can I add a dummy sub-type which gives a custom compile error message?

我(迄今为止未成功)的尝试是:

My (so far unsuccessful) attempt is:

struct TypeC {
    struct subtype {
        static_assert(false, "Attempt to access the non-existent subtype of TypeC.");
    };
};

但是 static_assert(false,...)不起作用,因为即使从未访问过该类型,编译器也会抛出错误。

But static_assert(false, ...) can't work, as the compiler throws the error even if the type is never accessed.

如何延迟对 static_assert 到访问该类型的时间?

How can I delay the evaluation of static_assert to the time when the type is being accessed?

失败的尝试是引入一个虚拟枚举并构造一个

A failed attempt is to introduce a dummy enum and construct an expression out of it:

enum { X };
static_assert(X != X, "...");






具体用例:我有一个类模板列表,由子类型 head tail 如果为非空,则为空时使用以下子类型,则应给出错误:


Concrete use case: I have a class-template List which is defined with the sub-types head and tail if non-empty, and should give an error if these sub-types are used if it is empty:

template<typename...>
struct List;

// empty list:
template<>
struct List<> {
    struct head { static_assert(false, "Attempt to access the head of an empty list."); };
    struct tail { static_assert(false, "Attempt to access the tail of an empty list."); };
};

// non-empty list:
template<typename Head, typename ...Tail>
struct List<Head, Tail...> {
    typedef Head head;
    typedef List<Tail...> tail;
};

如果我只是省略类型 head tail ,例如访问列表的第3个元素,其大小为2,代码为 List< int,int> :: tail :: tail :: head 给出的信息不太好(g ++ 4.7 .2):'head'不是'List< int> :: tail {aka List<>}''

If I simply leave out the types head and tail, when accessing e.g. the 3rd element of a list which has size 2 with the code List<int,int>::tail::tail::head gives the not so nice message (g++ 4.7.2): 'head' is not a member of 'List<int>::tail {aka List<>}'

推荐答案

// empty list:
template<typename... Args>
struct List {
    struct head {static_assert(sizeof...(Args) != 0, "Attempt to access the head of an empty list."); };
    struct tail {static_assert(sizeof...(Args) != 0, "Attempt to access the tail of an empty list."); };
};

// non-empty list:
template<typename Head, typename ...Tail>
struct List<Head, Tail...> {
    typedef Head head;
    typedef List<Tail...> tail;
};

编辑:此问题实际上涉及C ++模板如何工作的三个方面:

This problem actually touches on three aspects of how C++ templates work:



  1. (第14.7.1节[temp.inst] / p1)除非已明确实例化了类模板特殊化(14.7.2)或显式专门化(14.7.3),当在需要完全定义的对象类型的上下文中引用专门化时,或者当类类型的完整性影响程序的语义时,将隐式实例化类模板专门化。类模板专业化的隐式实例化导致声明的隐式实例化,而不是类成员函数,成员类的定义的隐式实例化。

  2. (第14.7.1节[temp.inst] / p11),实现不得隐式实例化...不需要实例化的类模板的成员类。 / li>
  3. (第14.6节[temp.res] / p8)如果无法为模板生成有效的专业化名称,并且未实例化该模板,则该模板格式错误,无需诊断。


3)表示 static_assert 表达式必须取决于模板参数,否则将无法为模板生成没有有效的专业名称,并且程序格式错误,并且编译器可以自由报告错误(尽管不必这样做)。在上面的代码中,可以为第一个模板生成有效的专业化,但是由于部分专业化,因此从未使用过这样的专业化。

3) means that the static_assert expression must depend on a template argument, as otherwise "no valid specialization" can be generated for the template and the program is ill-formed, and compilers are free to report an error (although they don't have to). In the above code, a valid specialization can be generated for the first template, but such a specialization is never used because of the partial specialization.

上面给出的解决方案也依赖于在1)和2)。 1)规定隐式实例化模板专门化仅实例化成员类的声明(而不是 definitions ),以及2)表示肯定禁止编译器尝试实例化 head tail (如果仅使用隐式实例化的 List<> 。请注意,如果您使用 template struct List <<> 显式实例化 List<> ,则此规则不适用。

The solution given above also relies on 1) and 2). 1) provides that implicitly instantiating a template specialization only instantiates the declarations (not definitions) of member classes, and 2) means that compilers are affirmatively prohibited from attempting to instantiate head or tail if one is merely using an implicitly instantiated List<>. Note that this rule does not apply if you explicitly instantiate List<> with template struct List<>;.

leemes答案中的解决方案之所以有效,是因为 typedef s不需要完整的类型,因此不会触发隐式实例化 SubTypeErrorMessage<> ),并在<$ c $中的 static_assert 中使用模板参数c> SubTypeErrorMessage 绕过3),因为可以为该模板生成有效的专业化名称(即 SubTypeErrorMessage< true> )。

The solution in leemes's answer works because typedefs do not require a complete type and so do not trigger implicit instantiation of SubTypeErrorMessage<> under 1), and the use of a template argument in the static_assert in SubTypeErrorMessage bypasses 3), as a valid specialization (i.e., SubTypeErrorMessage<true>) can be generated for that template.

值得注意的是,在两种情况下,实例化规则都意味着使用 List<> :: head TypeC :: subtype ,只要您不以要求完整类型的方式使用它们即可。因此,类似于 int f(List<> :: head&){返回0; } 是有效的,尽管完全没有意义,因为您实际上无法调用该函数。但是,如果您根本没有定义 List<> :: head ,则编译器将在此代码上报告(可能是隐秘的)错误。因此,这是更漂亮的错误消息的权衡:)

It's worth noting that in both cases the instantiation rules mean that it's still legal to use List<>::head or TypeC::subtype as long as you don't use them in a way that requires a complete type. Thus something like int f(List<>::head & ) { return 0; } is valid, though entirely meaningless since there's no way you can actually call that function. If you don't define List<>::head at all, however, the compiler will report a (perhaps cryptic) error on this code. So that's the trade-off for prettier error messages :)

这篇关于访问未定义子类型时的自定义编译错误消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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