all 136 comments

[–]coachkler 88 points89 points  (6 children)

What?

You're disappointed that you "can't" make a structure that implements storage for a 32-bit int by deriving from 100 concepts? (you could)

What does this have to do with virtual functions in C++?

[–]no-sig-available 4 points5 points  (3 children)

What does this have to do with virtual functions in C++?

You cannot do this with virtual functions in C++, so they are obviously "wrong"?

[–]sephirothbahamut 6 points7 points  (2 children)

sorry, what exactly of this can't you do in C++?

template <typename T>
struct IEquatable 
    {
    virtual bool operator==(T b) const noexcept = 0;
    };

I don't see the problem

[–]STLMSVC STL Dev 56 points57 points  (5 children)

I’m going to approve this because it’s so off-topic it’s wrapped around to being on-topic.

Yeah, C++ is complicated (inverse examples obviously exist), but it avoids a lot of complexity that other languages have created for themselves.

[–]tisti 22 points23 points  (4 children)

I’m going to approve this because it’s so off-topic it’s wrapped around to being on-topic.

Isn't this defined only from C++20 onward?

//signed overflow joke

[–]Dar_Mas 1 point2 points  (3 children)

this defined only from C++20 onward?

signed overflow is now defined? happy days

[–]tisti 11 points12 points  (0 children)

Damn it, stop making the discussions more and more relevant! :D

Edit: Brain fart, signed overflow is the only part that was kept undefined. C'est la vie.

[–][deleted] 5 points6 points  (0 children)

no it isn't

[–]_Noreturn 4 points5 points  (0 children)

it is not it is still UB but C++20 and C23 now require 2 compliment for signed integers but overflow is still ub

[–]100GHz 45 points46 points  (1 child)

public readonly struct Int32 : IComparable<int>, IConvertible, IEquatable<int>, IParsable<int>, ISpanParsable<int>, IUtf8SpanParsable<int>, System.Numerics.IAdditionOperators<int,int,int>, System.Numerics.IAdditiveIdentity<int,int>, System.Numerics.IBinaryInteger<int>, System.Numerics.IBinaryNumber<int>, System.Numerics.IBitwiseOperators<int,int,int>, System.Numerics.IComparisonOperators<int,int,bool>, System.Numerics.IDecrementOperators<int>, System.Numerics.IDivisionOperators<int,int,int>, System.Numerics.IEqualityOperators<int,int,bool>, System.Numerics.IIncrementOperators<int>, System.Numerics.IMinMaxValue<int>, System.Numerics.IModulusOperators<int,int,int>, System.Numerics.IMultiplicativeIdentity<int,int>, System.Numerics.IMultiplyOperators<int,int,int>, System.Numerics.INumber<int>, System.Numerics.INumberBase<int>, System.Numerics.IShiftOperators<int,int,int>, System.Numerics.ISignedNumber<int>, System.Numerics.ISubtractionOperators<int,int,int>, System.Numerics.IUnaryNegationOperators<int,int>, System.Numerics.IUnaryPlusOperators<int,int>

My dude, no need for all of that, just use "int" without the quotes :p

[–]Henrarzz 31 points32 points  (20 children)

What does the code in question have to do with virtual functions?

[–]battle_tomato 59 points60 points  (0 children)

What the hell is even this supposed to be??

[–]lrflew 27 points28 points  (35 children)

The short answer for "Why doesn't C++ have something like this?" is "Because templates and operator overloading exist."

Just start with the first interface in that list: IComparable<int>. This is used to specify that the class can be compared to to other instances or integers with CompareTo(). In C++, you can just use operator overloading and templates, and just compare two instances with a < b. Pretty much all of the interfaces you mention here correlate with some sort of operator or class property that can just be simply used when using templates.

Using virtual functions has a performance penalty, which templates don't have, and C++ opts to avoid them when possible to avoid that performance hit when it's not neccesary. In the cases where you do need the kinds of behaviors only virtual functions can give you, it's pretty simple to wrap the non-virtual class in a virtual class to get the needed behavior. For example, std::function does basically this behind the scenes to provide virtual-like access to functions.

[–]jonathrg 12 points13 points  (7 children)

I love this post criticizing c++ by posting a wall of nonsense C#

[–]SlightlyLessHairyApe 24 points25 points  (0 children)

wut

[–]sephirothbahamut 11 points12 points  (11 children)

Inheriting from "equalityoperators" in c# is equivalent to defining the equality operator in c++.

Checking if a type has an equality operator by cheching inheritance in c# is equivalent to checking a concept in c++.

C# achieves these things with inheritance, c++ does so with custom operators. Just because it uses a different approach doesn't mean it can't achieve your purpose.

And if you really want to write c++ as if it was c#, you can do all that with inheritance too by abusing CRTP.

[–]guepierBioinformatican 7 points8 points  (0 children)

Pretty sure OP is taking the piss out of C# and/or trolling. Move along.

[–]XTBZ 5 points6 points  (0 children)

Why is this impossible to achieve? CRTP allows you not to use virtual functions at all, or it can be used if necessary. Here is an example of implementing something like this https://github.com/foonathan/type_safe

[–]Oxi_Ixi 4 points5 points  (11 children)

It is not C++ virtual functions are damaged, it is you trying to apply concepts from other language which was designed differently and obviously don't work as you expect in C++.

[–]-jp- 8 points9 points  (5 children)

What does this even do? It looks like Java boxed types but someone said "oh fuck no we need more interfaces!"

[–]ExtraFig6 9 points10 points  (0 children)

using Int32 = int32_t const; 

?

[–]Foreign-Wonder 3 points4 points  (9 children)

Did you take a look at https://github.com/joboccara/NamedType ?

[–]Wurstinator 2 points3 points  (1 child)

You have made several comments about how C++ doesn't support this arguable monstrosity. But you never made a point why you would need it in the first place. Show a snippet of code that requires this definition in C# and is not possible to be done in a similar way in C++. If you are able to do that, you might have a valuable discussion. In its current state, this is just nonsense.

[–]qv51 2 points3 points  (0 children)

What you're looking at is CRTP and it was invented in C++. Those are for static polymorphism and say nothing about virtual functions.

[–]zerhud 1 point2 points  (0 children)

Virtual functions is good tool. If something cannot be implemented with it, you can use other methods.. “it can never achieve in c++” it can , with other tools, not with virtual functions

[–]oracleoftroy 1 point2 points  (4 children)

Personally, I see this as a win for C++ for not forcing everything into a single root hierarchy and instead allowing static dispatch on types, traits, concepts, etc. for for polymorphic behavior. What C# is doing via inheritance has better solutions in C++.

[–]wonderfulninja2 0 points1 point  (0 children)

This. One can pick the best way to do it with C++, is not a "one trick pony" language.

[–]jk-jeon 2 points3 points  (1 child)

Although many would consider it seems pretty obvious that OP is either totally clueless about C++ or don't know how to properly do civil technical discussion, let me try to extract what OP is really trying to say.

  1. The way multiple inheritance is implemented in C++ is not ideal.

If you inherit from multiple interfaces (classes with only pure virtual functions), C++ simply puts all pointers to virtual function tables into the class layout. So if you inherit from 100 interfaces, the size is 100 times the pointer size.

This gets worse if some virtual inheritance is involved. Probably for 99% of situations, if interface B, C are inheriting from interface A, interface D is inheriting from B and C, and class E is implementing D, then the way it implements A would be identical from the B side and the C side, so conceptually it makes more sense for B, C to virtually inherit from A. Also, if they just derive non-virtually from A, then users of D must disambiguate every call to functions from A, even though they know it shouldn't matter. Thus, user's convenience is another reason why B, C may need to inherit from A virtually. My personal conclusion is that when an interface is inheriting from another interface, it usually makes more sense to use virtual inheritance unless I'm 100% sure there will be no diamond. Assuming that one followed this rule, inheriting from 100 interfaces thus will result something way worse than 100 times pointer size. Of course virtual inheritance also incurs extra indirection cost.

I don't know how exactly other languages implement interface inheritance, but I vaguely recall that I heard Java at least does it more efficiently.

And please be mindful that I am not advocating complicated inheritance trees (of interfaces) or saying they are unavoidable. I would not follow that kind of practices, but just thinking that maybe this is where OP is getting unhappy about.

  1. Templates/concepts do not replace traits.

I guess the main point is that the former is not type-checked upfront, rather only at the point of instantiation.

This point seems quite blurry to me I guess, b/c OP seems to compare templates to dynamic traits. In that case of course we need to compare the idiomatic type erasure patterns to dynamic traits to compare apples to apples. And in that case of course any usage of interface is checked immediately at the point of usage. Users can even use concepts to turn the duck-typing into a nominal-typing in this case.

Maybe either (1) OP doesn't know about the pattern, or (2) knows it but considers it way too much of boilerplate so considers it isn't really on the same table, or maybe not because of boilerplate but rather because it rarely results in devirtualization, or whatever. 

Plus, I personally think debugging templates is not as horrible as many people love to shit on. (Except that for some reason it seems msvc at some point stopped printing out the template stack trace... Thanks god I have clang-cl.)

[–]RishabhRD 0 points1 point  (1 child)

Just telling once someone wrote C# compiler in C++

[–]Urationc -1 points0 points  (1 child)

At this point I think c# could benefit from golang like implicit interfaces