114
votes

No C++ love when it comes to the "hidden features of" line of questions? Figured I would throw it out there. What are some of the hidden features of C++?

4
  • @Devtron - I've seen some awesome bugs (i.e. unexpected behavior) sold as features. In fact, the games industry actually tries to make this happen nowadays and calls it "emergent gameplay" (also, check out "TK Surfing" from Psi-Ops, was purely a bug, then they left it as is and its one of the best features of the game IMHO) Commented Jan 6, 2010 at 15:11
  • 5
    @Laith J: Not very many people have read the 786-page ISO C++ standard from cover to cover -- but I suppose you have, and you've retained all of it, right? Commented Feb 14, 2010 at 19:20
  • 2
    @Laith, @j_random: See my question "What is a programmer's joke, how do I recognize it, and what is the appropriate response" at stackoverflow.com/questions/1/you-have-been-link-rolled. Commented Feb 26, 2010 at 8:57
  • See meta.stackexchange.com/questions/56669/…, meta.stackexchange.com/questions/57226/… and related Meta posts. Commented Jul 16, 2010 at 2:14

64 Answers 64

307
votes

Most C++ programmers are familiar with the ternary operator:

x = (y < 0) ? 10 : 20; 

However, they don't realize that it can be used as an lvalue:

(a == 0 ? a : b) = 1; 

which is shorthand for

if (a == 0) a = 1; else b = 1; 

Use with caution :-)

Sign up to request clarification or add additional context in comments.

9 Comments

Very interesting. I can see that making some unreadable code though.
Yikes. (a == 0 ? a : b) = (y < 0 ? 10 : 20);
(b ? trueCount : falseCount)++
Dunno if it's GCC specific, but I was surprised to find this also worked: (value ? function1 : function2)().
@Chris Burt-Brown: No, that should work everywhere if they have the same type (i.e. no defaulted arguments) function1 and function2 are implictly converted to function pointers, and the result is implicitly converted back.
|
238
votes

You can put URIs into C++ source without error. For example:

void foo() { http://stackoverflow.com/ int bar = 4; ... } 

11 Comments

But only one per function, i suspect? :)
@jpoh: http followed by a colon becomes a "label" which you use in a goto statement later. you get that warning from your compiler because it's not used in any goto statement in the above example.
You can add more than one as long as they have different protocols! ftp.microsoft.com gopher://aerv.nl/1 and so on...
@Pavel: An identifier followed by a colon is a label (for use with goto, which C++ does have). Anything following two slashes is a comment. Therefore, with http://stackoverflow.com, http is a label (you could theoretically write goto http;), and //stackoverflow.com is just an end-of-line comment. Both of these are legal C++, so the construct compiles. It doesn't do anything vaguely useful, of course.
Unfortunately goto http; doesn't actually follows the URL. :(
|
140
votes

Pointer arithmetics.

C++ programmers prefer to avoid pointers because of the bugs that can be introduced.

The coolest C++ I've ever seen though? Analog literals.

2 Comments

We avoid pointers because of bugs? Pointers are basically everything that dynamic C++ coding is about!
Analog literals are great for obfuscated C++ contest entries, especially the ASCII-art type.
119
votes

I agree with most posts there: C++ is a multi-paradigm language, so the "hidden" features you'll find (other than "undefined behaviours" that you should avoid at all cost) are clever uses of facilities.

Most of those facilities are not build-in features of the language, but library-based ones.

The most important is the RAII, often ignored for years by C++ developers coming from the C world. Operator overloading is often a misunderstood feature that enable both array-like behaviour (subscript operator), pointer like operations (smart pointers) and build-in-like operations (multiplying matrices.

The use of exception is often difficult, but with some work, can produce really robust code through exception safety specifications (including code that won't fail, or that will have a commit-like features that is that will succeed, or revert back to its original state).

The most famous of "hidden" feature of C++ is template metaprogramming, as it enables you to have your program partially (or totally) executed at compile-time instead of runtime. This is difficult, though, and you must have a solid grasp on templates before trying it.

Other make uses of the multiple paradigm to produce "ways of programming" outside of C++'s ancestor, that is, C.

By using functors, you can simulate functions, with the additional type-safety and being stateful. Using the command pattern, you can delay code execution. Most other design patterns can be easily and efficiently implemented in C++ to produce alternative coding styles not supposed to be inside the list of "official C++ paradigms".

By using templates, you can produce code that will work on most types, including not the one you thought at first. You can increase type safety,too (like an automated typesafe malloc/realloc/free). C++ object features are really powerful (and thus, dangerous if used carelessly), but even the dynamic polymorphism have its static version in C++: the CRTP.

I have found that most "Effective C++"-type books from Scott Meyers or "Exceptional C++"-type books from Herb Sutter to be both easy to read, and quite treasures of info on known and less known features of C++.

Among my preferred is one that should make the hair of any Java programmer rise from horror: In C++, the most object-oriented way to add a feature to an object is through a non-member non-friend function, instead of a member-function (i.e. class method), because:

  • In C++, a class' interface is both its member-functions and the non-member functions in the same namespace

  • non-friend non-member functions have no privileged access to the class internal. As such, using a member function over a non-member non-friend one will weaken the class' encapsulation.

This never fails to surprise even experienced developers.

(Source: Among others, Herb Sutter's online Guru of the Week #84: http://www.gotw.ca/gotw/084.htm )

6 Comments

+1 very thorough answer. it's incomplete for obvious reasons (otherwise there wouldn't be "hidden features" anymore!) :p in the first point at the end of the answer, you mentioned members of a class interface. do you mean ".. is both its member-functions and the friend non-member functions"?
what you are mentioning about with 1 must be koenig lookup, right?
@wilhelmtell: No no no... :-p ... I DO mean "its member-functions and NON-FRIEND non-member functions".... Koenig's Lookup will make sure these functions will be considered sooner than other "outside" functions in its search for symbols
Great post, and +1 especially for the last part, which far too few people realize. I'd probably add the Boost library as a "hidden feature" as well. I pretty much consider it the standard library that C++ should have had. ;)
|
117
votes

One language feature that I consider to be somewhat hidden, because I had never heard about it throughout my entire time in school, is the namespace alias. It wasn't brought to my attention until I ran into examples of it in the boost documentation. Of course, now that I know about it you can find it in any standard C++ reference.

namespace fs = boost::filesystem; fs::path myPath( strPath, fs::native ); 

3 Comments

I guess this is useful if you don't want to use using.
It's also useful as a way to switch between implementations, whether selecting say thread-safe versus non-thread-safe, or version 1 versus 2.
It's especially useful if you're working on a very large project with large namespace hierarchies and you don't want your headers to cause namespace pollution (and you want your variable declarations to be human-readable).
102
votes

Not only can variables be declared in the init part of a for loop, but also classes and functions.

for(struct { int a; float b; } loop = { 1, 2 }; ...; ...) { ... } 

That allows for multiple variables of differing types.

8 Comments

Nice to know that you can do it, but personally I'd really try to avoid doing anything like that. Mostly because it's difficult to read.
Actually, what would work in this context is using a pair: for ( std::pair<int,float> loop=std::make_pair(1,2); loop.first > 0; loop.second+=1)
@Valentin well then I recommend you to try and make a bugreport against VS2008 instead of downvoting the hidden feature. It's clearly the fault of your compiler.
Hmm, it doesn't work in msvc10 either. How sad :(
@avakar in fact, gcc introduced a bug that makes it reject too, in v4.6 :) see gcc.gnu.org/bugzilla/show_bug.cgi?id=46791
|
77
votes

The array operator is associative.

A[8] is a synonym for *(A + 8). Since addition is associative, that can be rewritten as *(8 + A), which is a synonym for..... 8[A]

You didn't say useful... :-)

6 Comments

Actually, when using this trick, you should really pay attention to what type you are using. A[8] is actually the 8th A while 8[A] is the Ath integer starting at address 8. If A is a byte, you have a bug.
you mean "commutative" where you say "associative"?
Vincent, you're wrong. The type of A doesn't matter at all. For example, if A were a char*, the code would still be valid.
Beware that A must be a pointer, and not a class overloading operator[].
Vincent, in this there has to be one integral type and one pointer type, and neither C nor C++ cares which one goes first.
|
72
votes

C++ is a standard, there shouldn't be any hidden features...

C++ is a multi-paradigm language, you can bet your last money on there being hidden features. One example out of many: template metaprogramming. Nobody in the standards committee intended there to be a Turing-complete sublanguage that gets executed at compile-time.

Comments

72
votes

One thing that's little known is that unions can be templates too:

template<typename From, typename To> union union_cast { From from; To to; union_cast(From from) :from(from) { } To getTo() const { return to; } }; 

And they can have constructors and member functions too. Just nothing that has to do with inheritance (including virtual functions).

8 Comments

Interesting! So, must you initialise all members? Does it follow the usual struct order, implying that the last member will be initialised "on top of" earlier members?
j_random_hacker oh, right that's nonsense. good catch. i wrote it as it would be a struct. wait i'll fix it
Doesn't this invoke undefined behavior?
@gbacon, yes it does invoke undefined behavior if From and To are set and used accordingly. Such an union can be used with defined behavior though (with To being an array of unsigned char or a struct sharing an initial sequence with From). Even if you use it in an undefined way, it might still be useful for low-level work. Anyway, this is just one example of an union template - there may be other uses for an templated union.
Careful with the constructor. Note that you are required to only construct the first element, and it's only allowed in C++0x. As of the current standard, you have to stick to trivially constructible types. And no destructors.
|
65
votes

Another hidden feature that doesn't work in C is the functionality of the unary + operator. You can use it to promote and decay all sorts of things

Converting an Enumeration to an integer

+AnEnumeratorValue 

And your enumerator value that previously had its enumeration type now has the perfect integer type that can fit its value. Manually, you would hardly know that type! This is needed for example when you want to implement an overloaded operator for your enumeration.

Get the value out of a variable

You have to use a class that uses an in-class static initializer without an out of class definition, but sometimes it fails to link? The operator may help to create a temporary without making assumptins or dependencies on its type

struct Foo { static int const value = 42; }; // This does something interesting... template<typename T> void f(T const&); int main() { // fails to link - tries to get the address of "Foo::value"! f(Foo::value); // works - pass a temporary value f(+Foo::value); } 

Decay an array to a pointer

Do you want to pass two pointers to a function, but it just won't work? The operator may help

// This does something interesting... template<typename T> void f(T const& a, T const& b); int main() { int a[2]; int b[3]; f(a, b); // won't work! different values for "T"! f(+a, +b); // works! T is "int*" both time } 

Comments

61
votes

Lifetime of temporaries bound to const references is one that few people know about. Or at least it's my favorite piece of C++ knowledge that most people don't know about.

const MyClass& x = MyClass(); // temporary exists as long as x is in scope 

8 Comments

Can you elaborate? As is you're just teasing ;)
ScopeGuard (ddj.com/cpp/184403758) is a great example that leverages this feature.
I am with Joseph Garvin. Please enlighten us.
I just did in the comments. Besides, it's a natural consequence of using a const reference parameter.
|
52
votes

A nice feature that isn't used often is the function-wide try-catch block:

int Function() try { // do something here return 42; } catch(...) { return -1; } 

Main usage would be to translate exception to other exception class and rethrow, or to translate between exceptions and return-based error code handling.

9 Comments

I don't think you can return from catch block of Function Try, only rethrow.
I just tried compiling the above, and it gave no warning. I think the above example works.
return is only banned for constructors. A constructor's function try block will catch errors initializing the base and members (the only case where a function try block does something different than just having a try inside the function); not re-throwing would result in an incomplete object.
Yes. This is very useful. I wrote macros BEGIN_COM_METHOD and END_COM_METHOD to catch exceptions and return HRESULTS so that exceptions didn't leak out of a class implementing a COM interface. It worked well.
As pointed out by @puetzk, this is the only way to handle exceptions thrown by anything in a constructor's initialiser list, such as base classes' constructors or those of data members.
|
44
votes

Many know of the identity / id metafunction, but there is a nice usecase for it for non-template cases: Ease writing declarations:

// void (*f)(); // same id<void()>::type *f; // void (*f(void(*p)()))(int); // same id<void(int)>::type *f(id<void()>::type *p); // int (*p)[2] = new int[10][2]; // same id<int[2]>::type *p = new int[10][2]; // void (C::*p)(int) = 0; // same id<void(int)>::type C::*p = 0; 

It helps decrypting C++ declarations greatly!

// boost::identity is pretty much the same template<typename T> struct id { typedef T type; }; 

1 Comment

Interesting, but initially I actually had more trouble reading some of those definitions. Another way to fix the inside-out problem with C++ declarations is to write some template type aliases: template<typename Ret,typename... Args> using function = Ret (Args...); template<typename T> using pointer = *T; -> pointer<function<void,int>> f(pointer<function<void,void>>); or pointer<void(int)> f(pointer<void()>); or function<pointer<function<void,int>>,pointer<function<void,void>>> f;
42
votes

A quite hidden feature is that you can define variables within an if condition, and its scope will span only over the if, and its else blocks:

if(int * p = getPointer()) { // do something } 

Some macros use that, for example to provide some "locked" scope like this:

struct MutexLocker { MutexLocker(Mutex&); ~MutexLocker(); operator bool() const { return false; } private: Mutex &m; }; #define locked(mutex) if(MutexLocker const& lock = MutexLocker(mutex)) {} else void someCriticalPath() { locked(myLocker) { /* ... */ } } 

Also BOOST_FOREACH uses it under the hood. To complete this, it's not only possible in an if, but also in a switch:

switch(int value = getIt()) { // ... } 

and in a while loop:

while(SomeThing t = getSomeThing()) { // ... } 

(and also in a for condition). But i'm not too sure whether these are all that useful :)

9 Comments

Neat! I never knew you could do that... it would have (and will) save some hassle when writing code with error return values. Is there any way to still have a conditional instead of just != 0 in this form? if((int r = func()) < 0) doesn't seem to work...
puetzk, no there isn't . but glad you like it :)
@Frerich, this is not possible in C code at all. I think you are thinking of if((a = f()) == b) ..., but this answer actually declares a variable in the condition.
@Angry it's very different, because the variable declaration is tested for its boolean value straight away. There is a mapping to for-loops too, which looks like for(...; int i = foo(); ) ...; This will go through the body as long as i is true, initializing it each time again. The loop that you show is simply demonstrating a variable declaration, but not a variable declaration that simultanuously acts as a condition :)
Very good, except you didn't mention the intended use for this feature was for dynamic pointer casts, I believe.
|
29
votes

Preventing comma operator from calling operator overloads

Sometimes you make valid use of the comma operator, but you want to ensure that no user defined comma operator gets into the way, because for instance you rely on sequence points between the left and right side or want to make sure nothing interferes with the desired action. This is where void() comes into game:

for(T i, j; can_continue(i, j); ++i, void(), ++j) do_code(i, j); 

Ignore the place holders i put for the condition and code. What's important is the void(), which makes the compiler force to use the builtin comma operator. This can be useful when implementing traits classes, sometimes, too.

1 Comment

I just used this to finish off my overkill expression ignorer. :)
28
votes

Array initialization in constructor. For example in a class if we have a array of int as:

class clName { clName(); int a[10]; }; 

We can initialize all elements in the array to its default (here all elements of array to zero) in the constructor as:

clName::clName() : a() { } 

3 Comments

You can do this with any array anywhere.
@Potatoswatter: harder than it looks, due to the most vexing parse. I can't think of anywhere else it can be done, except maybe a return value
If the type of the array is a class type, then this isn't needed right?
27
votes

Oooh, I can come up with a list of pet hates instead:

  • Destructors need to be virtual if you intend use polymorphically
  • Sometimes members are initialized by default, sometimes they aren't
  • Local clases can't be used as template parameters (makes them less useful)
  • exception specifiers: look useful, but aren't
  • function overloads hide base class functions with different signatures.
  • no useful standardisation on internationalisation (portable standard wide charset, anyone? We'll have to wait until C++0x)

On the plus side

  • hidden feature: function try blocks. Unfortunately I haven't found a use for it. Yes I know why they added it, but you have to rethrow in a constructor which makes it pointless.
  • It's worth looking carefully at the STL guarantees about iterator validity after container modification, which can let you make some slightly nicer loops.
  • Boost - it's hardly a secret but it's worth using.
  • Return value optimisation (not obvious, but it's specifically allowed by the standard)
  • Functors aka function objects aka operator(). This is used extensively by the STL. not really a secret, but is a nifty side effect of operator overloading and templates.

6 Comments

pet hate: no defined ABI for C++ apps, unlike C ones that everyone uses because every language can guarantee to call a C function, no-one can do the same for C++.
Destructors need to be virtual only if you intend to destroy polymorphically, which is a little subtly different from the first point.
With C++0x local types can be used as template parameters.
With C++0x, destructors will be virtual if the object has any virtual functions (i.e. a vtable).
don't forget NRVO, and of course any optimization is allowed as long as it doesn't change the program output
|
26
votes

You can access protected data and function members of any class, without undefined behavior, and with expected semantics. Read on to see how. Read also the defect report about this.

Normally, C++ forbids you to access non-static protected members of a class's object, even if that class is your base class

struct A { protected: int a; }; struct B : A { // error: can't access protected member static int get(A &x) { return x.a; } }; struct C : A { }; 

That's forbidden: You and the compiler don't know what the reference actually points at. It could be a C object, in which case class B has no business and clue about its data. Such access is only granted if x is a reference to a derived class or one derived from it. And it could allow arbitrary piece of code to read any protected member by just making up a "throw-away" class that reads out members, for example of std::stack:

void f(std::stack<int> &s) { // now, let's decide to mess with that stack! struct pillager : std::stack<int> { static std::deque<int> &get(std::stack<int> &s) { // error: stack<int>::c is protected return s.c; } }; // haha, now let's inspect the stack's middle elements! std::deque<int> &d = pillager::get(s); } 

Surely, as you see this would cause way too much damage. But now, member pointers allow circumventing this protection! The key point is that the type of a member pointer is bound to the class that actually contains said member - not to the class that you specified when taking the address. This allows us to circumvent checking

struct A { protected: int a; }; struct B : A { // valid: *can* access protected member static int get(A &x) { return x.*(&B::a); } }; struct C : A { }; 

And of course, it also works with the std::stack example.

void f(std::stack<int> &s) { // now, let's decide to mess with that stack! struct pillager : std::stack<int> { static std::deque<int> &get(std::stack<int> &s) { return s.*(pillager::c); } }; // haha, now let's inspect the stack's middle elements! std::deque<int> &d = pillager::get(s); } 

That's going to be even easier with a using declaration in the derived class, which makes the member name public and refers to the member of the base class.

void f(std::stack<int> &s) { // now, let's decide to mess with that stack! struct pillager : std::stack<int> { using std::stack<int>::c; }; // haha, now let's inspect the stack's middle elements! std::deque<int> &d = s.*(&pillager::c); } 

1 Comment

26
votes

Another hidden feature is that you can call class objects that can be converted to function pointers or references. Overload resolution is done on the result of them, and arguments are perfectly forwarded.

template<typename Func1, typename Func2> class callable { Func1 *m_f1; Func2 *m_f2; public: callable(Func1 *f1, Func2 *f2):m_f1(f1), m_f2(f2) { } operator Func1*() { return m_f1; } operator Func2*() { return m_f2; } }; void foo(int i) { std::cout << "foo: " << i << std::endl; } void bar(long il) { std::cout << "bar: " << il << std::endl; } int main() { callable<void(int), void(long)> c(foo, bar); c(42); // calls foo c(42L); // calls bar } 

These are called "surrogate call functions".

2 Comments

When you say overload resolution is done on the result of them, do you mean it actually converts it to both Functors and then does overload resolution? I tried printing something in operator Func1* (), and operator Func2* (), but it seems to pick the correct one when it figures out which conversion operator to invoke.
@navigator, yep it conceptually converts to both and then picks the best. It does not need to actually call them, because it knows from the result-type what they will yield already. The actual call is done when it turns out what was finally picked.
26
votes

Hidden features:

  1. Pure virtual functions can have implementation. Common example, pure virtual destructor.
  2. If a function throws an exception not listed in its exception specifications, but the function has std::bad_exception in its exception specification, the exception is converted into std::bad_exception and thrown automatically. That way you will at least know that a bad_exception was thrown. Read more here.

  3. function try blocks

  4. The template keyword in disambiguating typedefs in a class template. If the name of a member template specialization appears after a ., ->, or :: operator, and that name has explicitly qualified template parameters, prefix the member template name with the keyword template. Read more here.

  5. function parameter defaults can be changed at runtime. Read more here.

  6. A[i] works as good as i[A]

  7. Temporary instances of a class can be modified! A non-const member function can be invoked on a temporary object. For example:

    struct Bar { void modify() {} } int main (void) { Bar().modify(); /* non-const function invoked on a temporary. */ } 

    Read more here.

  8. If two different types are present before and after the : in the ternary (?:) operator expression, then the resulting type of the expression is the one that is the most general of the two. For example:

    void foo (int) {} void foo (double) {} struct X { X (double d = 0.0) {} }; void foo (X) {} int main(void) { int i = 1; foo(i ? 0 : 0.0); // calls foo(double) X x; foo(i ? 0.0 : x); // calls foo(X) } 

4 Comments

P Daddy: A[i] == *(A+i) == *(i+A) == i[A]
I get the commutation, it's just that this means that [] has no semantic value of its own and is simply equivalent to a macro-style replacement where "x[y]" is replaced with "(*((x) + (y)))". Not at all what I expected. I wonder why it's defined this way.
Backward compatibility with C
Regarding your first point: There's one particular case where you have to implement a pure virtual function: pure virtual destructors.
24
votes

map::operator[] creates entry if key is missing and returns reference to default-constructed entry value. So you can write:

map<int, string> m; string& s = m[42]; // no need for map::find() if (s.empty()) { // assuming we never store empty values in m s.assign(...); } cout << s; 

I'm amazed at how many C++ programmers don't know this.

7 Comments

And on the opposite end you cannot use operator[] on a const map
+1 for Nick, people can go insane if they don't know about .find().
or "const map::operator[] generates error messages"
Not a feature of the language, it is a feature of the Standard template library. It is also pretty obvious, since operator[] returns a valid reference.
I had to use maps in C# for while, where maps don't behave that way, in order to realize that this is a feature. I thought I was annoyed by it more than I used it, but it seems I was wrong. I'm missing it in C#.
|
20
votes

Putting functions or variables in a nameless namespace deprecates the use of static to restrict them to file scope.

5 Comments

"deprecates" is a strong term…
@Potato: Old comment, I know, but the standard does say the use of static in namespace scope is deprecated, with preference for unnamed namespaces.
@GMan: no prob, I don't think SO pages really "die." Just for both sides of the story, static in global scope is not deprecated in any way. (For reference: C++03 §D.2)
Ah, on closer reading, "A name declared in the global namespace has global namespace scope (also called global scope)." Does that really mean that?
@Potato: Yup. :) static use should only be used within a class-type or function.
19
votes

Defining ordinary friend functions in class templates needs special attention:

template <typename T> class Creator { friend void appear() { // a new function ::appear(), but it doesn't … // exist until Creator is instantiated } }; Creator<void> miracle; // ::appear() is created at this point Creator<double> oops; // ERROR: ::appear() is created a second time! 

In this example, two different instantiations create two identical definitions—a direct violation of the ODR

We must therefore make sure the template parameters of the class template appear in the type of any friend function defined in that template (unless we want to prevent more than one instantiation of a class template in a particular file, but this is rather unlikely). Let's apply this to a variation of our previous example:

template <typename T> class Creator { friend void feed(Creator<T>*){ // every T generates a different … // function ::feed() } }; Creator<void> one; // generates ::feed(Creator<void>*) Creator<double> two; // generates ::feed(Creator<double>*) 

Disclaimer: I have pasted this section from C++ Templates: The Complete Guide / Section 8.4

Comments

18
votes

void functions can return void values

Little known, but the following code is fine

void f() { } void g() { return f(); } 

Aswell as the following weird looking one

void f() { return (void)"i'm discarded"; } 

Knowing about this, you can take advantage in some areas. One example: void functions can't return a value but you can also not just return nothing, because they may be instantiated with non-void. Instead of storing the value into a local variable, which will cause an error for void, just return a value directly

template<typename T> struct sample { // assume f<T> may return void T dosomething() { return f<T>(); } // better than T t = f<T>(); /* ... */ return t; ! }; 

Comments

17
votes

Read a file into a vector of strings:

 vector<string> V; copy(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(V)); 

istream_iterator

3 Comments

Or: vector<string> V((istream_iterator<string>(cin)), istream_iterator<string>);
you mean vector<string> V((istream_iterator<string>(cin)), istream_iterator<string>());-- missing parentheses after second param
This isn't really a hidden C++ feature. More of an STL feature. STL != a language
14
votes

You can template bitfields.

template <size_t X, size_t Y> struct bitfield { char left : X; char right : Y; }; 

I have yet to come up with any purpose for this, but it sure as heck surprised me.

1 Comment

See here, where I recently suggested it for n-bit arithmetic: stackoverflow.com/questions/8309538/…
14
votes

One of the most interesting grammars of any programming languages.

Three of these things belong together, and two are something altogether different...

SomeType t = u; SomeType t(u); SomeType t(); SomeType t; SomeType t(SomeType(u)); 

All but the third and fifth define a SomeType object on the stack and initialize it (with u in the first two case, and the default constructor in the fourth. The third is declaring a function that takes no parameters and returns a SomeType. The fifth is similarly declaring a function that takes one parameter by value of type SomeType named u.

6 Comments

is there any difference between 1st and 2nd? though, I know they are both initializations.
Comptrol: I don't think so. Both will end up calling the copy-constructor, even though the first one LOOKS like the assignment operator, it is really the copy-constructor.
If u is a different type from SomeType, then the first one will call the conversion constructor first and then the copy constructor, whereas the second one will only call the conversion constructor.
1st is implicit call of constructor, 2nd is explicit call. Look at the following code to see the difference: #include <iostream> class sss { public: explicit sss( int ) { std::cout << "int" << std::endl; }; sss( double ) { std::cout << "double" << std::endl; }; }; int main() { sss ddd( 7 ); // calls int constructor sss xxx = 7; // calls double constructor return 0; }
True - the first line will not work if the constructor is declared explicit.
|
12
votes

Getting rid of forward declarations:

struct global { void main() { a = 1; b(); } int a; void b(){} } singleton; 

Writing switch-statements with ?: operators:

string result = a==0 ? "zero" : a==1 ? "one" : a==2 ? "two" : 0; 

Doing everything on a single line:

void a(); int b(); float c = (a(),b(),1.0f); 

Zeroing structs without memset:

FStruct s = {0}; 

Normalizing/wrapping angle- and time-values:

int angle = (short)((+180+30)*65536/360) * 360/65536; //==-150 

Assigning references:

struct ref { int& r; ref(int& r):r(r){} }; int b; ref a(b); int c; *(int**)&a = &c; 

6 Comments

FStruct s = {}; is even shorter.
In the last example, it would be simpler with: a(); b(); float c=1.0f;
This syntax "float c=(a(),b(),1.0f);" is useful for accenting the assigment-operation (assigment of "c"). Assigment-operations are important in programming because they are less likely to become deprecated IMO. Don't know why, might be something to do with functional programming where program state is re-assigned every frame. PS. And no, "int d = (11,22,1.0f)" will be equal to "1". Tested a minute ago with VS2008.
+1 Shouldn't you be calling main? I'd suggest global().main(); and just forget about the singleton (you can just work with the temporary, which gets it's lifetime extended)
I doubt assigning references is portable. I love the struct to waive forward declarations though.
|
12
votes

The ternary conditional operator ?: requires its second and third operand to have "agreeable" types (speaking informally). But this requirement has one exception (pun intended): either the second or third operand can be a throw expression (which has type void), regardless of the type of the other operand.

In other words, one can write the following pefrectly valid C++ expressions using the ?: operator

i = a > b ? a : throw something(); 

BTW, the fact that throw expression is actually an expression (of type void) and not a statement is another little-known feature of C++ language. This means, among other things, that the following code is perfectly valid

void foo() { return throw something(); } 

although there's not much point in doing it this way (maybe in some generic template code this might come handy).

1 Comment

For what it's worth, Neil has a question on this: stackoverflow.com/questions/1212978/… , just for extra info.
12
votes

The dominance rule is useful, but little known. It says that even if in a non-unique path through a base-class lattice, name-lookup for a partially hidden member is unique if the member belongs to a virtual base-class:

struct A { void f() { } }; struct B : virtual A { void f() { cout << "B!"; } }; struct C : virtual A { }; // name-lookup sees B::f and A::f, but B::f dominates over A::f ! struct D : B, C { void g() { f(); } }; 

I've used this to implement alignment-support that automatically figures out the strictest alignment by means of the dominance rule.

This does not only apply to virtual functions, but also to typedef names, static/non-virtual members and anything else. I've seen it used to implement overwritable traits in meta-programs.

1 Comment

Neat. Any particular reason you included struct C in your example...? Cheers.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.