Why is there not an std::is_struct type trait?
Asked Answered
Y

5

63

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?

Yount answered 22/9, 2015 at 14:40 Comment(5)
Structs and classes are not "almost the same thing"; they are in fact exactly the same type of thing, but they happen to have two different syntactic representations with different default modes of access specification. In a type declaration that makes all access specifiers explicit, i.e. (struct|class) MyClass : public BaseClass { private: .... , there is no difference between struct and class.Calash
The highly upvoted selected answer is plain wrong, disinformation. There are now two correct answers, one short and one long. Please select a correct answer so that readers coming here via Google are not completely misled and misinformed.Riojas
@Cheersandhth.-Alf: What do you claim is wrong with it? Are you saying that C++ has a concept of "struct" that's actually separate and distinct from the concept of "class"? Or are you arguing that the answer should say that structs and classes are the same thing with two keywords to declare them, instead of saying that structs aren't a thing in C++? Or do you have some other issue with it?Schnitzler
@user2357112: C++ indeed has a concept of "struct". It's far more specialized than the one of "class", which includes unions and polymorphic classes etc. For standard quotes etc. see, for example, my answer. By the way these are not claims. They are facts. Also, there's more wrong with the selected answer than just its home-brewed terminology and lack of logic.Riojas
@Cheersandhth.-Alf Reading your answer, I actually agree with Lightness's comment. struct does not imply POD struct.Calash
C
183

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.

Cosmopolis answered 5/12, 2015 at 16:54 Comment(45)
C++ has structs because it contains C, which has structs. There are zero C++ compilers that do not accept the struct keyword. Therefore struct is a C++ feature. The fact that it got it from C is not sufficient to state that it's not a C++ feature. Are curly brackets also not C++?Tayib
@WarrenP: C++ does not "contain" C. The definition of classes in C++ is very clear. C++ does not have structs. The entire point I am making is that the keyword struct creates classes, not structs. Your assertion "therefore struct is a C++ feature" is a non sequitur, I'm afraid.Cosmopolis
It implements almost everything C implement, including everything that structs can do, then, provides facilities, then? So then, it contains Struct, and struct is part of C++. Is a struct (when used in a C++ source file) that has no member methods, and is used, as a C struct might be used, for say, a packed bit-field, no longer a struct although it functions identically to a C struct when used to encode a series of bit fields?Tayib
@WarrenP: It doesn't implement everything C implements. For example, it does not implement Variable Length Arrays, or structs. "Is a struct (when used in a C++ source file) that has no member methods, and is used, as a C struct might be used, for say, a packed bit-field, no longer a struct although it functions identically to a C struct when used to encode a series of bit fields?" That is correct. It is a class. The behaviour of the std::is_class trait makes this abundantly clear.Cosmopolis
I never thought of it that way. Is this how his holiness, the Bjarne would explain it too?Tayib
@WarrenP: You'd have to ask him. But, given that he was the one who began this change in CFront 2.0 back in 1985 ("a structure is a class that..."), I'd say it's a fairly good bet.Cosmopolis
If so, then why forward-declaring a type as struct and actually defining it as class, or vice versa, yields a compiler warning (if not an error)?Granddaddy
@VioletGiraffe: It doesn't, at least not in GCC. If some other compiler warns (e.g. I haven't checked MSVS), then it's for the same reason that you get any warning: because it looks like you may have written something you didn't intend. After all, why would you switch between the two keywords? It would be one of the more silly warnings, though, because such a mistake couldn't possibly be of less consequence.Cosmopolis
@LightnessRacesinOrbit: fair enough. Either MSVC or clang (OS X) throws that warning, I don't remember which.Granddaddy
@sp2danny: That's not true, as stated two comments up.Cosmopolis
Just for completeness: There are also no chars, ints, floats or doubles in C++; there are also no functions, pointers or arrays. There is no if/then/else; actually there is nothing at all which you know from C. That these concepts bear the same names in C++ which other, different entities bear in C is irrelevant. And now you may wonder whether you have fallen for Poe's law.Coherence
@PeterA.Schneider: "There are also no chars, ints, floats or doubles in C++; there are also no functions, pointers or arrays. There is no if/then/else" Yes, there are all of those things. But there are no structs. If you think there is "no clear indicator of [my] intent" with this answer, you should read it a few more times because it's perfectly clear.Cosmopolis
Well, the current ISO C++ standard talks of "structures" and "struct types" for types defined with the keyword "struct". So when you say "In fact, C++ has not had structs since 1985"; what you must mean with "no structs" is: "no C structs". Because C++ has, in common language, keyword and standard, clearly structs. I therefore assume that you want to emphasize that in spite of the syntactic and semantic similarities, which are by design, C and C++ are different languages to a degree that things like struct or int which bear the same name in both languages are still different.Coherence
@PeterA.Schneider: Each occurrence of that, I submit, is an editorial problem. The standard is very clear that struct declares a class. That is one, single concept. C++ does not have structs, or structures. At all. Feel free to write your own competing answer, but I have to go now and will be unable to further entertain this discussion.Cosmopolis
You are funny. So the keyword struct is misleading (because it doesn't define one), the standard is full of errors and the language people use is incorrect and badly written. I see.Coherence
adding to that: a struct in C++ can have virtual member functions.Schismatic
@Alex Because it's a class, not a struct!Cosmopolis
@PeterA.Schneider: Yes, that is (mostly) correct. C++ has not had structures since 1985. There are a few instances of editorial sloppiness in the standard where "a struct" is used, but actually you may be surprised at how few of these instances there are. The standard is clear: the keyword struct (which indeed exists only for compatibility) introduces a class. Period. I can well believe that many people get this wrong but that's not my fault, nor is it my problem!Cosmopolis
@BarryTheHatchet jep I know. but that should make it pretty clear that structs in C++ are actually a superset of what structs in C can do.Schismatic
@Alex: C++ does not have structs. C++ has classes. In C++, struct introduces a class. This is the point.Cosmopolis
@Alex: Yes it's 100% a wording problemCosmopolis
@BarryTheHatchet I deleted the last comment before this gets even more out of hand.Schismatic
@BarryTheHatchet To me, the case is exactly inverse. Whenever you use the keyword class, you are in fact declaring a struct. That the standard mentions classes at all is editorial sloppiness. C++ has not had classes since 1979 (the new synonym for struct was introduced by Bjarne to get funding -- it sounds fashionable). That's not my fault, and not my problem. :-)Coherence
@Alex I don't see how any of this is "out of hand" but okayCosmopolis
@PeterA.Schneider: Bjarne certainly did not introduce the term "class", though I will concede that SIMULA was not exactly in widespread use when Bjarne "borrowed" its OOP model for C++Cosmopolis
@BarryTheHatchet what I mean by that is I didn't intend to get a wording argument out of it rather than I was trying to say there is an easy way to proof that writing struct in C++ gets you more than you might expect.Schismatic
@Alex: Like using the std::is_class type trait? ;)Cosmopolis
This whole discussion is completely silly. Of course C++ has structs. It's just a different kind of thing than C's structs.Sumrall
@kralyk: No, it doesn't. As explained above. Did you have anything of substance to add to the discussion?Cosmopolis
@LightnessRacesinOrbit Anything of substance to add? Sorry, but this discussion as well as your post has had no substance to begin with - from the start you operate on the term "to have structs" without any solid definition for it. Apparently you mean "the same structs C has" and of course I agree C++ doesn't have those. But that doesn't mean it doesn't have structs, it has and it also calls them classes. Other languages also have structs a they also are different things (such as in Rust). You are probably looking for the term POD (Plain Old Data), and those can be created in C++.Sumrall
@kralyk: By that logic we can just make up random names for things and claim that we're right. C++ doesn't have unicorns? Sure it does! But it calls them classes. No. POD is indeed the closest concept C++ has to what C called structs, but POD classes are still classes. You seem to dispute that the naming matters, but on a question asking why there is not a trait named is_struct, I think it matters indeed. Because that's literally what the question is about.Cosmopolis
@LightnessRacesinOrbit C++ indeed doesn't have unicorns (there's no mention of unicorns in the standard), but it does have structs. The is_class function name could arbitrarily also be is_struct. I don't see how C++ is different in this regard from, say, Go or Rust. Both Go and Rust have structs (and definitely no classes), but these are different from C structs (and also one another). Or do you mean to tell me Go and Rust don't have structs too? What is it that they have then???Sumrall
@kralyk: That makes no sense at all. C++ doesn't have unicorns, but it has structs? What makes unicorns less special than structs? Your only defence of this claim is that "is_class could have been is_struct". But it isn't! Just as it isn't is_unicorn. Then you go on about some other, unrelated languages...?Cosmopolis
@LightnessRacesinOrbit Unicorns aren't defined in the C++ standard as a thing in C++, but structs are. It's as simple as that. I mentioned the other languages, because they also have structs, just as C++ has, and because their structs are different from C's structs, just like in C++. I hope it's clearer now.Sumrall
@kralyk: No, structs are not, and I've explained why several times. Merely saying "yes they are" is not constructive.Cosmopolis
@LightnessRacesinOrbit I haven't merely said it, I backed it up with the C++ standard, which defines the struct keyword and it also defines POD structs as a subset of classes. You on the other hand haven't really explained anything, you haven't even explained what "to have structs" means to you - what does that even mean? Also, how is C++ different from the two languages I mentioned that "have structs"?Sumrall
@kralyk: I've addressed every single one of those points already; I will not rehash this again. Let's agree to disagree. Feel free to write a competing answer.Cosmopolis
@LightnessRacesinOrbit No you haven't, you still haven't answered what it means when you say "language X has/hasn't structs". (This is the third time I'm asking.) You should edit this into your answer because otherwise nobody knows what you meant. There's not much to agree/disagree on unless you clarify that. Oh and BTW I just found this line in the standard in §3.2: struct X; // declare X as a struct type I think that's pretty clear.Sumrall
−1 “In fact, C++ has not had structs since 1985. It only has classes.” is just wrong. E.g. C++11 §9/8 defines “A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key class” (the distinction here is between struct and union class types).Riojas
@Cheersandhth.-Alf: Er, yes, exactly... "is a class". Thank you for bringing me your typical festive cheer, Alf.Cosmopolis
Okay, look at this way: "there are no unions, only classes". The standard defines unions as a class type. But "there are no unions" is clearly nonsense. [Oh, that brings to light a secondary error in this answer: that it limits class types to those defined with a struct and class. Contrary to that assertion, a union is also a class. Summing up, this answer is nonsense through and through.]Riojas
This answer is wrong on MS Windows : types declared with the struct keyword have a different name mangling than types declared with the class keyword. Here for instance the function that takes a struct void f(const foo&) is mangled into ?f@@YAXABUfoo@@@Z and the function that takes a class void g(const bar&) is mangled into ?g@@YAXABVbar@@@Z. (Notice the U / V before the name of the function). This has implications if you ship a header with just a forward-declaration and the implementation is another type (missing symbols, etc.)Jokjakarta
@Jean-MichaëlCelerier: The question is about C++, not about MS Windows. A C++ developer relying on implementation details on a specific platform is, whether realising it or not, developing in more than just C++. I will be happy to give a different answer to a different question that asks about implementation differences.Cosmopolis
Still, all that said, I'm curious - do you even use the struct keyword at all in your C++ code? Might be the same, standard-wise, but how about programmer-wise? Do you write your traits with class, not struct templates? Do you represent your POD with classes?Twelvemonth
@Twelvemonth Actually I do! Usually things like exception types when I'm not really adding any members, or PODdy things.Cosmopolis
C
27

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.

Charlena answered 22/9, 2015 at 14:53 Comment(2)
You should probably add something to indicate (in accordance with the answer you cited) that for full struct/class semantic equality, base-class access specifiers must also be explicit, e.g. struct Derived : Base { is equivalent to class Derived : public Base { public:, and class Derived : Base { is equivalent to struct Derived : private Base { private:.Calash
I think you misunderstood what @KyleStrand was saying. I think he was trying to say that a class C privately inherits from its bases by default, and a struct S publicly inherits from its bases by default. You've written that subclasses of a class C privately inherit from it by default, and that subclasses of a struct S publically inherit from it by default, which isn't the case.Schnitzler
B
25

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.

Brana answered 22/9, 2015 at 14:56 Comment(5)
bool isClass = std::is_class<T>::value; is also evaluated at compile time. It should be a problem to evaluate std::is_struct.Ripieno
What would the point of distinguishing between them be? What advantage could it provide? You can't do anything with one that you can't also do with the other. They are the same thing.Brana
@RobK The point would be to communicate with a different language, or to overlay memory-mapped registers with a POD struct.Coherence
@PeterA.Schneider What is unclear about "They are the same thing"? A class and a struct are the same thing.Brana
They are the same, as far as the C++ language and the keywords are concerned. But funny enough, struct is more often used for simple POD classes, and in natural language the word "struct[ure]" is more often used for PODs or standard layout classes. So some people do distinguish, intuitively perhaps. You also asked "What would the point of distinguishing between them be?". Answer: One may want to tell C compatible aggregates from others (see my answer to the OP). The language could have chosen to use the keywords struct and class for that distinction, but it didn't.Coherence
C
15

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
Coherence answered 29/2, 2016 at 11:53 Comment(6)
I would be willing to compromise and accept that C++, from a standardese perspective per the prose you cite, may have a notion of "structs" that is a subset of "classes". However, by the very passage you quote as 9/8, this is not equivalent to "the set of types that people can define using the keyword struct". I would go further and say that these supposed standardese meaning of "struct" is neither widely-recognised nor unambiguous enough for a trait type to be named after it.Cosmopolis
@LightnessRacesinOrbit Re "I would be willing to compromise": Took you a while.Coherence
I read your answer for the first time ever last night. So, actually, it took me about 4 minutes. Is that acceptable to you, or would you like to continue venturing into personal attack land for no reason?Cosmopolis
@LightnessRacesinOrbit I felt a little mocking was in order because you made a claim ("C++ has not had structs since 1985") which was obviously wrong in all conceivable meanings, as I pointed out in comments and my post. That's why you couldn't give a reason for it; your main argument was no reason but simply an apodictic re-assertion: "The kind of types that you declare with the keyword class and the keyword struct are classes. Period." That -- as you correctly point out -- the choice of keyword does not make a difference is not a counter-argument. [t.b.c.]Coherence
@LightnessRacesinOrbit ... Any historical rationale is, if at all relevant, an argument for structs in C++, not against them. So you refuse to accept all substantial arguments and evidence presented in comments, and only after my writing a meticulously prepared, detailed, evidence-laced step-by step rebuttal you say half a year later you "would" be willing to "compromise". I think that limited amount of insight could have come earlier.Coherence
It sounds like you're shocked that a well-thought-out, properly-written counter-argument might be more persuasive and convincing than some angry, unsubstantiated "no you're wrong" in the comments section. I can't really comprehend that shock. By moving a small way towards accepting some of your premises (and let's be clear: compromising to reach a middle ground does not mean "I no longer believe in what I have said") I have commended your answer. Can't you just take that positively, instead of arguing and fighting and being negative? Gees.Cosmopolis
R
6
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.

Riojas answered 22/12, 2016 at 1:51 Comment(2)
While you make a fair and interesting point about the standard's definition of "POD struct" (which I consider an editorial problem inherited from history, but I don't intend to argue that here), I think it's a reasonable assumption to make that the OP is talking about the keywords class vs struct. As such, I propose adding a note to your answer that this definition of "struct" is not the one the OP was referring to (maybe).Cosmopolis
@LightnessRacesinOrbit: A note that maybe the OP had a meaningless interpretation of his words, in mind, would IMHO not help.Riojas

© 2022 - 2024 — McMap. All rights reserved.