Typedef Changes Meaning

typedef changes meaning

g++ is correct and conforming to the standard. From [3.3.7/1]:

A name N used in a class S shall refer to the same declaration in its
context and when re-evaluated in the completed scope of S. No
diagnostic is required for a violation of this rule.

Before the typedef, A referred to the ::A, however by using the typedef, you now make A refer to the typedef which is prohibited. However, since no diagnostic is required, clang is also standard conforming.

jogojapan's comment explains the reason for this rule.
Take the following change to your code:

template<class T>
class A
{};

template<class T>
class B
{
public:
A a; // <-- What "A" is this referring to?
typedef A<T> A;
};

Because of how class scope works, A a; becomes ambiguous.

changes meaning from typedef

Because of the way that type aliases work, it looks to your compiler like you're trying to define MyClass::String inside of itself. It gets all confused.

[C++11: 7.1.3/6]: In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. [..]

Here's a complete example:

typedef int alias_t;

class T
{
typedef alias_t alias_t;
};

Output:

test.cpp:4: error: declaration of 'typedef alias_t T::alias_t'

test.cpp:1: error: changes meaning of 'alias_t' from 'typedef int alias_t'


I can fix this example by adding the :: prefix to the existing type:

typedef int alias_t;

class T
{
typedef ::alias_t alias_t;
};

In your code, that translates to the following:

class MyClass
{
typedef ::String String;
};

Declaration of method changes meaning of symbol

gcc is correct, but clang is not required to give a diagnostic (3.3.7):

A name N used in a class S shall refer to the same declaration in its
context and when re-evaluated in the completed scope of S. No
diagnostic is required for a violation of this rule.

This is because of how class scope works. The foo of void foo(); is visible within the entire scope of the class A, so the declaration of void foo(); changes the meaning of foo in the typedef from referring to struct foo to the name of the function foo.

unqlite.h:651:15: error: changes meaning of ‘pgno’ from ‘typedef sxu64 pgno’ [-fpermissive]

This issue appears when including unqlite.h in a C++ file, and compiling it with a g++. You have two solutions to fix it:

  • You can get the patch produced by the author of the lib as indicated here: https://github.com/symisc/unqlite/issues/24
  • Or you can edit the unqlite.h file on line 661 replacing:

pgno pgno; /* Page number for this page */

by

::pgno pgno; /* Page number for this page */

Which will work with a g++ compiler as well as other C++ compilers. Of course, you cannot compile unqlite.c with this edited header, with a C compiler.

If you want to learn more about this error, this might be a good link: typedef changes meaning

Have a good day!

C++ - meaning of a statement combining typedef and typename

typedef is defining a new type for use in your code, like a shorthand.

typedef typename _MyBase::value_type value_type;
value_type v;
//use v

typename here is letting the compiler know that value_type is a type and not a static member of _MyBase.

the :: is the scope of the type. It is kind of like "is in" so value_type "is in" _MyBase. or can also be thought of as contains.

typedef struct vs struct definitions

The common idiom is using both:

typedef struct S { 
int x;
} S;

They are different definitions. To make the discussion clearer I will split the sentence:

struct S { 
int x;
};

typedef struct S S;

In the first line you are defining the identifier S within the struct name space (not in the C++ sense). You can use it and define variables or function arguments of the newly defined type by defining the type of the argument as struct S:

void f( struct S argument ); // struct is required here

The second line adds a type alias S in the global name space and thus allows you to just write:

void f( S argument ); // struct keyword no longer needed

Note that since both identifier name spaces are different, defining S both in the structs and global spaces is not an error, as it is not redefining the same identifier, but rather creating a different identifier in a different place.

To make the difference clearer:

typedef struct S { 
int x;
} T;

void S() { } // correct

//void T() {} // error: symbol T already defined as an alias to 'struct S'

You can define a function with the same name of the struct as the identifiers are kept in different spaces, but you cannot define a function with the same name as a typedef as those identifiers collide.

In C++, it is slightly different as the rules to locate a symbol have changed subtly. C++ still keeps the two different identifier spaces, but unlike in C, when you only define the symbol within the class identifier space, you are not required to provide the struct/class keyword:

 // C++
struct S {
int x;
}; // S defined as a class

void f( S a ); // correct: struct is optional

What changes are the search rules, not where the identifiers are defined. The compiler will search the global identifier table and after S has not been found it will search for S within the class identifiers.

The code presented before behaves in the same way:

typedef struct S { 
int x;
} T;

void S() {} // correct [*]

//void T() {} // error: symbol T already defined as an alias to 'struct S'

After the definition of the S function in the second line, the struct S cannot be resolved automatically by the compiler, and to create an object or define an argument of that type you must fall back to including the struct keyword:

// previous code here...
int main() {
S();
struct S s;
}

why use c++ Typedef?

that isn't a good example because Integer and int don't have any different meaning. But imagine something like,

typedef int BlamoType;

Now, in the future when 'blamo' becomes a 64-bit integer, you can just change this typedef, and all other code stays the same.

Another reason for typedef is to shorten type names. For example instead of

boost::shared_ptr<std::map<std::wstring, std::vector<int> > > blamo1;
boost::shared_ptr<std::map<std::wstring, std::vector<int> > > blamo2;
std::vector<boost::shared_ptr<std::map<std::wstring, std::vector<int> > > > blamoList;

you can do:

typedef boost::shared_ptr<std::map<std::wstring, std::vector<int> > > BlamoType;
BlamoType blamo1;
BlamoType blamo2;
std::vector<BlamoType> blamoList;

Why should we typedef a struct so often in C?

As Greg Hewgill said, the typedef means you no longer have to write struct all over the place. That not only saves keystrokes, it also can make the code cleaner since it provides a smidgen more abstraction.

Stuff like

typedef struct {
int x, y;
} Point;

Point point_new(int x, int y)
{
Point a;
a.x = x;
a.y = y;
return a;
}

becomes cleaner when you don't need to see the "struct" keyword all over the place, it looks more as if there really is a type called "Point" in your language. Which, after the typedef, is the case I guess.

Also note that while your example (and mine) omitted naming the struct itself, actually naming it is also useful for when you want to provide an opaque type. Then you'd have code like this in the header, for instance:

typedef struct Point Point;

Point * point_new(int x, int y);

and then provide the struct definition in the implementation file:

struct Point
{
int x, y;
};

Point * point_new(int x, int y)
{
Point *p;
if((p = malloc(sizeof *p)) != NULL)
{
p->x = x;
p->y = y;
}
return p;
}

In this latter case, you cannot return the Point by value, since its definition is hidden from users of the header file. This is a technique used widely in GTK+, for instance.

UPDATE Note that there are also highly-regarded C projects where this use of typedef to hide struct is considered a bad idea, the Linux kernel is probably the most well-known such project. See Chapter 5 of The Linux Kernel CodingStyle document for Linus' angry words. :) My point is that the "should" in the question is perhaps not set in stone, after all.

Please explain syntax rules and scope for typedef

Basically a typedef has exactly the same syntax as an object declaration except that it is prefixed with typedef. Doing that changes the meaning of the declaration so that the new identifier declares an alias for the type that the object that would have been declared, had it been a normal declaration, would have had.

A typedef is scoped exactly as the object declaration would have been, so it can be file scoped or local to a block or (in C++) to a namespace or class.

e.g.

Declares an int:

int a;

Declares a type that is an alias for int:

typedef int a_type;

Declares a pointer to a char:

char *p;

Declares an alias for a char *:

typedef char *pChar;

Declares a function pointer:

int (*pFn)(int);

Declares an alias for the type that is 'pointer to a function taking int and returning int':

typedef int (*pFunc)(int);


Related Topics



Leave a reply



Submit