Why is there not an std::is_struct type trait?

C++C++11Typetraits

C++ Problem Overview


I've seen that in order to check if a type T is a class I can use:

bool isClass = std::is_class<T>::value;

It returns true for both classes and structs. I know that in C++ they are almost the same thing, but I'd like to know why there's not a distinction between them in the type trait. Is it always useless to check this difference, or is there some more reason that I don't understand?

C++ Solutions


Solution 1 - C++

> It returns true for both classes and structs. I know that in C++ they are almost the same thing, but I'd like to know why there's not a distinction between them in the type trait.

Unfortunately this is a common misconception in C++. Sometimes it comes from fundamental misunderstanding, but at other times it comes from an ambiguity in English. It can come from inaccurate compiler diagnostics, badly-written books, incorrect SO answers…

You've probably read something like this:

"There is no difference in C++ between a struct and a class except the default visibility of members and bases."

This passage can be interpreted in a sense that is misleading, because the notions of identity and equality are hard to distinguish when using phrases like "no difference".

In fact, C++ has not had structs since 1985. It only has classes.

The kind of types that you declare with the keyword class and the keyword struct are classes. Period. The keyword struct, and the visibility rules that are the default when defining a class using that keyword, were kept only for backward compatibility with C … but that's a syntax thing. It doesn't make the resulting types actually be of a different kind.

The type trait makes no distinction because there literally isn't one to make.

Solution 2 - C++

It is impossible to distinguish a difference in semantics for empty definitions like

class C {
public:

};

from

struct S {

};

or similarly

class C {

};

and

struct S {
private:

};

Apart from the struct vs class keyword, there is no behavioral difference detectable. See also this Q&A.

Note: As remarked by @KyleStrand, derivation also requires explicit access specifiers, so S : private Base {}; and C : Base {}; are equivalent, the same as S : Base {}; and C : public Base {};, where S is a struct, C is a class, and Base can be either.

Solution 3 - C++

They're the same thing. The only difference (default member visibility) exists only at compile time. There is otherwise no difference at all between struct and class.

ETA: What you probably want is std::is_pod, which will tell you if your class is a "plain old data type". Much of the discussion and commentary on this question seems to indicate that this is what those who think there should be a distinction actually want.

Solution 4 - C++

Others have pointed out correctly that in C++ the keywords struct and class have the same meaning except for the difference in member visibility.

Whether you call aggregate types thus defined "structs" or "classes" or "weiruewzewiruz" is up to you. In the interest of communication it is generally advisable to follow established conventions, so I would advise against "weiruewzewiruz".

It is also recommended to use semantic differences as a guideline for word choices. The use of struct is more common for simple aggregated data which does not have a lot of internal logic and invariants; a typical use would be struct point { float x; float y; };. Such types are often called "structs" or "structures" in the literature. It would not be surprising if somebody using fprintf in C++ referred to the first argument as a "pointer to a FILE struct". FILE is an example of what Scott Meyers' means in "More Effective C++", Item 34:

> It is safe to assume that a structure definition that compiles in both > languages [C and C++ -p.a.s] is laid out the same way by both compilers.

Concerning the natural language, the word choice "structure" is not coincidental: Meyers is talking about a plain old data aggregate which has identical semantics in both languages, right down to the bit level.

Concerning programming language, it wouldn't matter if the C++ definition of the data aggregate in question used the keyword struct or class (with a public access specifier). struct is perhaps the more natural choice, because the C++ semantics of the aggregate are the semantics of a C struct. Also, using struct enables both C and C++ sources to easier share one type definition.

The C++ standard uses "struct" and "structure" both in natural and programming language, not only in cases of interoperability: 1.7/5: "A structure declared as", or 3.2/4 struct X; // declare X as a struct type. Most interesting is 9/8, laying ground for interop-criteria:

> 8 A standard-layout struct is a standard-layout class > defined with the class-key struct or the class-key > class. [...]

How anybody reading this can claim that there are no structs in C++ is beyond me. This clearly is not an editing error because the terms "struct" and "class" are explicitly set in relation to each other.


More interesting than word choices and matters of taste, however, are manifest, testable differences. Under what circumstances is a C++ aggregate comparable to and compatible with a C struct? Perhaps this question was underlying your question? The standard layout mentioned in the quote is the criteria. It is detailed in 9/7 and essentially prescribes that

  • only one class in an inheritance hierarchy may have non-static data member definitions (probably because the standard does not want to specify the order of data elements defined at different levels in such a hierarchy);
  • no virtual functions or virtual base classes are allowed (because of the additional instance data necessary for the run-time information);
  • all members have the same "access control" (either public, protected or private; probably because an implementation is free to order by access control).

The standard then says

> 9 [ Note: Standard-layout classes are useful for communicating > with code written in other programming languages. > Their layout is specified in 9.2.—end note ]

Of course a struct definition which compiles in C fulfills these criteria, hence Scott Meyers' assertion. FILE from stdio.h is a prominent, not-quite-trivial example. Note that the standard doesn't make guarantees because object layout is implementation dependent and may change just with a compiler option.

Whether a class has standard layout can be tested with the type trait std::is_standard_layout<T>. The following program, which is inspired by an example on cppreference, checks the main cases laid out in the standard.

#include <cstdio>
#include <typeinfo>
#include <type_traits>

using namespace std;

struct funcOnlyT // fine
{
	int f();
};

class podT {   // "class" is ok
    int m1;
	int m2;
};
 
struct badAccessCtrlT { // bad: public/private
    int m1;
private:
    int m2;
};
 
struct polymorphicT {  // bad: polymorphic
    int m1;
	int m2;
    virtual void foo();
};
 
 
struct inheritOkT: podT // ok: inheritance, data only on one level
{
	int f();
};


struct inheritPlusDataT: podT // bad: inheritance, data on 2 levels
{
	int m3;
};

template<typename T1, typename T2>
struct templT     // ok with std layout types T1, T2
{
	T1 m1;
	T2 m2;
};

// print type "name" and whether it's std layout
template<typename T>
void printIsStdLayout()
{
    printf("%-20s: %s\n", 
			typeid(T).name(),
			std::is_standard_layout<T>::value 
				? "is std layout" 
				: "is NOT std layout");
}

int main()
{
    printIsStdLayout<funcOnlyT>();
    printIsStdLayout<podT>();
    printIsStdLayout<badAccessCtrlT>();
    printIsStdLayout<polymorphicT>();
    printIsStdLayout<inheritOkT>();
    printIsStdLayout<inheritPlusDataT>();
    printIsStdLayout<templT<int, float> >();
    printIsStdLayout<FILE>();
}

Sample session:

$ g++ -std=c++11 -Wall -o isstdlayout isstdlayout.cpp && ./isstdlayout
9funcOnlyT          : is std layout
4podT               : is std layout
14badAccessCtrlT    : is NOT std layout
12polymorphicT      : is NOT std layout
10inheritOkT        : is std layout
16inheritPlusDataT  : is NOT std layout
6templTIifE         : is std layout
9__sFILE64          : is std layout

Solution 5 - C++

C++11 §9/8 ([class]/8):
> A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key class. A standard-layout union is a standard-layout class defined with the class-key union.

C++11 §9/10 ([class]/10):
> A POD struct is a non-union class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). […]

Since a POD struct is a standard-layout class it is a subset of standard-layout struct. As far as I know this is the most general meaning of struct in the C++ standard. And so presumably what you're looking for is a type trait or set of type traits that lets you identify a standard-layout struct as such.

And voilà, looking at the list of type traits there's is_class and is_standard_layout. When a type satisifies ¹both it's a “struct”. Or more precisely, it's a standard-layout struct, as defined by C++11 §9/8.


Regarding

> I'd like to know why there's not a distinction between [class and struct] in the type trait

Well, there is. That's the is_standard_layout trait.


Regarding

> Is it always useless to check this difference, or is there some more reason that I don't understand?

No, it's not useless to check this difference. The standard defines standard-layout for the reason that it's very practically useful. As the standard itself notes,

C++11 §9/9 ([class]/9):
> [Note: Standard-layout classes are useful for communicating with code written in other programming languages. Their layout is specified in 9.2.—end note ]


Notes:
¹ The is_class trait is true for a class or struct, but not for a union, even though the standard defines that “a union is a class”. I.e. the trait is more specific than the general terminology.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJepessenView Question on Stackoverflow
Solution 1 - C++Lightness Races in OrbitView Answer on Stackoverflow
Solution 2 - C++TemplateRexView Answer on Stackoverflow
Solution 3 - C++Rob KView Answer on Stackoverflow
Solution 4 - C++Peter - Reinstate MonicaView Answer on Stackoverflow
Solution 5 - C++Cheers and hth. - AlfView Answer on Stackoverflow