Why is there not an std::is_struct type trait?
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?
Solution 1:
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:
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:
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:
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