Tag vs. Type Names

Tag vs. Type Names C treats tags as second class types. C++ isn‘t much kinder. Here‘s how to give them first-class treatment in both languages.

Identifiers are among the most basic elements of programming languages. Languages use them to name entities such as functions, objects, constants, and types.

In C and C++, an identifier is a contiguous sequence of characters starting with a letter or underscore and followed by zero or more letters, underscores, or digits. Identifiers are case sensitive, so that DAN, Dan, and dan are distinct identifiers.

For as long as I‘ve known C, I‘ve been struck by the curious way that C treats identifiers for structures, unions, and enumerations as second-class citizens. An identifier that names a structure, union, or enumeration names a type, yet you cannot use the identifier to refer to that type in the same way that you use a typedef name.

This month, I‘ll look at this quirky behavior and how it carries over into C++ as behavior that‘s cleaner on the surface but messier underneath. I‘ll also suggest a simple programming style that tidies up the mess in both languages.

Tag names in C

In C, the name s appearing in:


struct s
  {
  ...
  };

is a tag. A tag by itself is not a type name. If it were, then C compilers would accept declarations such as:


s x;  	// error in C
s *p;  	// ditto

But they don‘t. You must write the declarations as:


struct s x;  // OK
struct s *p;  // OK

The combination of struct and s-in that order-is called an elaborated type specifier.

The names of unions and enumerations are also tags rather than types. For example:


enum day { Sunday, Monday, ... };
...
day today;  	// error
enum day tomorrow;	// OK

Here,enum day is the elaborated type specifier.

For the most part, C does not permit different entities to have the same name in the same scope. For example, when these two declarations appear in the same scope:


int status(); 	// function
int status;  	// object

the compiler will flag the second one as an error. But C treats tags differently than it does other identifiers. C compilers hold tags in a symbol table that‘s conceptually, if not physically, separate from the table that holds all other identifiers. Thus, it‘s possible for a C program to have both a tag and an another identifier with the same spelling in the same scope.

For example, C compilers will accept both:


int status();		// function
enum status { ... };	// enumeration

in the same scope. They will even accept:

struct s s;

which declares object s of type struct s. Such declarations may not be good practice, but they are C.

Tags and typedefs

Many programmers (including yours truly) prefer to think of struct tags as type names, so they define an alias for the tag using a typedef. For example, defining:


struct s
  {
  ...
  };
typedef struct s T;

lets you use T in place of struct s, as in:


T x;	// OK
T *p;	// OK

A program cannot use T as the name of both a type and an object (or a function or enumeration constant), as in:

T T;  // error

This is good.

The tag name in a struct, union, or enum definition is optional. Many programmers fold the struct definition into the typedef and dispense with the tag altogether, as in:


typedef struct
  {
  ...
  } T;

This works well, except in self-referential structures containing pointers to structures of the same type. For example:


struct list_node
  {
  ...
  struct list_node *next;
  };

defines a struct called list_node, which contains, among other things, a pointer to another list_node. If you wrap the struct definition in a typedef and omit the tag, as in:


typedef struct
  {
  ...
  list_node *next; // error
  } list_node;

the compiler will complain because the declaration for member next refers to list_node before list_node is declared.

With a self-referential struct, you have no choice but to declare a tag for the struct. If you prefer to use a typedef name thereafter, you must declare both the tag and the typedef. Many programmers follow naming conventions suggested by Kernighan and Ritchie.[1] In the first edition of their book, they use a short, somewhat cryptic, identifier for the tag, and a longer uppercase identifier for the typedef, as in:


typedef struct tnode
  {
  ...
  struct tnode *left;
  struct tnode *right;
  } TREENODE;

For the second edition, they changed TREENODE to Treenode.[2]

I‘ve never understood why they use different names for the tag and the typedef when one name will do just fine:

typedef struct tree_node tree_node;

What‘s more, you can write this definition before, rather than after, the struct definition, as in:


typedef struct tree_node tree_node;
struct tree_node
  {
  ...
  tree_node *left;
  tree_node *right;
  };

You need not use the keyword struct in declaring members, such as left and right, that refer to other tree_nodes.

Tags in C++

The syntax for classes in C++ is an extension of the syntax for structs and unions. In fact, C++ considers structs and unions just as special cases of classes; so I will, too.

Although the C++ standard doesn‘t call them tags, class names act very much like tags. For example, you can declare an object of class string with a declaration such as:

class string s;

Of course, few, if any, C++ programmers actually do this.

C++ was designed so that user-defined types can look very much like built-in types. Declarations using built-in types, such as:

int n;

don‘t use the keyword class, so using the keyword class in the declaration for s just above only serves to remind readers that string is not a built-in type. Therefore, C++ lets you omit the keyword class and use class names as if they were type names, as in:

string s;

Again, the C++ standard never utters the word tag. In C++, class and enumeration names are just type names. However, there are several rules that single out these type names for special treatment. I find it easier to continue to refer to class and enumeration names as tags.

If you want, you can imagine that for each tag, C++ automatically generates a typedef with the same spelling. For example, when the compiler encounters a class definition such as:


class gadget
  {
  ...
  };

it automatically generates a typedef, as if the program defined:

typedef class gadget gadget;

Unfortunately, this is not entirely accurate. C++ can‘t generate such typedefs for structs, unions or enums without introducing incompatibilities with C.

For example, suppose a C program declares both a function and a struct named query:


int query();
struct query;

Again, this may be bad practice, but it might happen if the function declaration and struct declaration reside in separate headers by different authors. In any event, given these declarations, query by itself refers to the function and struct query refers to the type.

If C++ automatically generated typedefs for tags, then when you compile this program as C++, the compiler would generate:

typedef struct query query;

Unfortunately, this type name would conflict with the function name, and the program would not compile.

In C++, tags act just like typedef names, except that a program can declare an object, function, or enumerator with the same name and the same scope as a tag. In that case, the object, function, or enumerator name hides the tag name and the program can refer to the tag name only by using the keyword class, struct, union, or enum (as appropriate) in front of the tag name. Thus, a C program that contains both:


int query();
struct query;

behaves the same when compiled as C++. Again, the name query alone refers to the function. The elaborated type specifier struct query refers to the type.

The way to avoid accidentally hiding a tag with a non-type name is to intentionally hide the tag name with a typedef name. That is, each time you declare a tag, you should also define a typedef with the same spelling as an alias for the tag, as in:


typedef class gadget gadget;
class gadget
  {
  ...
  };

Then, if there‘s a function, object, or enumeration constant with the same name in the same scope, you‘ll get an overt error message from the compiler, rather than a potentially subtle bug you‘ll have to track down at run time.

Recommended guidelines

What I suggest is that you adopt a uniform style for turning tags into typedefs.

For each tag, define a typedef name with the same spelling in the same scope as an alias for the tag.

This style works equally well in both C and C++.

For each class, you can place the typedef definition either before or after the class definition. (Again, classes in C++ include structs and unions.) Placing the typedef before the class definition lets you use the typedef even inside the class, so that‘s what I recommend.

For each enumeration, you must place the typedef after the enumeration definition, as in:


enum day { Sunday, Monday, ... };
typedef enum day day;  // OK

Placing the typedef before the enumeration definition provokes a compile-time error:


typedef enum day day;  // error
enum day { Sunday, Monday, ... };

Admittedly, the incidence of errors arising from tag name hiding appears pretty small. You may never run afoul of these problems. But if an error in your software might cause bodily injury or death, then you should write the typedefs no matter how unlikely the error.

I can‘t imagine why anyone would ever want to hide a class name with a function or object name in the same scope as the class. The hiding rules in C were a mistake, and they shouldn‘t have been extended to classes in C++. You can compensate for the mistake, but it requires more programming effort than should have been necessary.

Dan Saks is the president of Saks & Associates, a C/C++ training and consulting company. He served for many years as secretary of the C++ standards committee. With Thomas Plum, he wrote C++ Programming Guidelines. You can write to him at [email protected].

时间: 2024-11-12 10:38:47

Tag vs. Type Names的相关文章

Type name is discouraged. By convention, Java type names usually start with an uppercase letter

在Eclipse中添加类的时候,提示如下信息: 如下图: 原因是输入的类名"Name"首字母没有大写,把首字母改为大写的就好了.

JSP TAG

Jsp tag 可以灵活的将公共JSP代码模块化,类似<jsp:include page="./include.jsp"></jsp:include>等等. 我们自定义的JSP模块代码可能在引用时会根据不同的引用场景,需要显示的内容略有不同,这时使用JSP.INCLUDE就不能满足我们的需求了. 因此,我们可以使用JSP tag 的方式实现. 第一步 在WEB-INF/tags目录下创建 demo.tag 文件 代码如下: <%@tag pageEncod

原创:js代码, 让dedecms支持Tag选择, 添加内容更为方便,不用手输Tag

dedecms在编辑修改内容时,TAG标签需要手动输,中文的Tag, 中间还得用半角字符','分隔,  输入法切来切去很不方便,   于是动手改后台代码, 利用后台的tags_main.php, 让dedecms添加内容时能去tags_main里去选择. 1. 打开\dede\templets\album_add.htm文件, 在顶部head中的js script区增加js方法. function setag(){ var tagg=window.showModalDialog("tags_ma

A Step-by-Step Guide to Your First AngularJS App

What is AngularJS? AngularJS is a JavaScript MVC framework developed by Google that lets you build well structured, easily testable, and maintainable front-end applications. And Why Should I Use It? If you haven't tried AngularJS yet, you're missing

关于bootstrap-datetimepicker 插件的配置参数详解

本人在网上查找的,  觉得还不错,就抄过来了... 有错误大家一起讨论,谢谢... 原地址是:http://www.bootcss.com/p/bootstrap-datetimepicker/ 项目  此项目是bootstrap-datetimepicker 项目 的一个分支,原项目不支持 Time 选择.其它部分也进行了改进.增强,例如load 过程增加了对 ISO-8601 日期格式的支持.文档是拷贝/粘贴字原项目的文档,并且加入了更多细节说明. 别犹豫了,下载下来试试吧 ! 下载 ZIP

ElasticSearch 嵌套映射和过滤器及查询

ElasticSearch - 嵌套映射和过滤器 Because nested objects are indexed as separate hidden documents, we can’t query them directly. Instead, we have to use the nested query to access them: GET /my_index/blogpost/_search { "query": { "bool": { &quo

【转】Styling And Animating SVGs With CSS

原文转自:http://www.smashingmagazine.com/2014/11/03/styling-and-animating-svgs-with-css/?utm_source=CSS-Weekly&utm_campaign=Issue-135&utm_medium=email CSS can be used to style and animate scalable vector graphics, much like it is used to style and ani

C++ Core Guidelines

C++ Core Guidelines September 9, 2015 Editors: Bjarne Stroustrup Herb Sutter This document is a very early draft. It is inkorrekt, incompleat, and pµøoorly formatted. Had it been an open source (code) project, this would have been release 0.6. Copy

Apache Avro? 1.7.6 Specification

Apache Avro? 1.7.6 Specification Introduction Schema Declaration Primitive Types Complex Types Records Enums Arrays Maps Unions Fixed Names Aliases Data Serialization Encodings Binary Encoding Primitive Types Complex Types JSON Encoding Sort Order Ob