22

Possible Duplicate:
C++ Static member method call on class instance

Today I discovered that something I had long (and I mean long—like, for twenty years), thought illegal in C++ is actually legal. Namely, calling a static member function as if it belonged to an individual object. For example:

struct Foo
{
    static void bar() { cout << "Whatever."; }
};

void caller()
{
    Foo foo;
    foo.bar();    // Legal -- what?
}

I normally see static member functions being called strictly with "scope resolution syntax," thus:

Foo::bar();

This makes sense, because a static member function is not associated with any particular instance of the class, and therefore we wouldn't expect a particular instance to be syntactically "attached" to the function call.

Yet I discovered today that GCC 4.2, GCC 4.7.1, and Clang 3.1 (as a random sampling of compilers) accept the former syntax, as well as:

Foo* foo = new Foo;
foo->bar();

In my particular case, the legality of this expression led to a runtime error, which convinced me that the peculiarity of this syntax is of more than academic interest—it has practical consequences.

Why does C++ allow static member functions to be called as if they were direct members of individual objects—that is, by using the . or -> syntax attached to an object instance?

Community
  • 1
  • 1
OldPeculier
  • 11,049
  • 13
  • 50
  • 76
  • Hmmm, why not? The difference lies in the instance passed as implicit parameter as with member functions or even not. The function pointer itself might be accessible for the compiler either ways. – πάντα ῥεῖ Aug 15 '12 at 21:01
  • 3
    "In my particular case, the legality of this expression led to a runtime error" can you expand on that please? – Luchian Grigore Aug 15 '12 at 21:02
  • I remember getting a compiler error when I do this when the function is declared in the .h and defined in the .cpp – Topo Aug 15 '12 at 21:03
  • Yes @LuchianGrigore, I'll try, but it's kind of complicated. Basically it appeared during a refactor. I had a class that had previously offered a non-static load() function. Later I refactored that function to be static and to return a pointer to a newly created object—basically, I converted a loading function that worked on an existing object into a factory function that created _and_ loaded the object. (More after the break...) – OldPeculier Aug 15 '12 at 21:14
  • ...Then I went about adjusting callers to this function so that they were hip with the program. But I missed one case, so that particular caller didn't store the resulting pointer, which resulted in an error. The erroneous caller looked like: `Foo* foo = new Foo; foo->load();` But the new semantics wanted, `Foo* foo = Foo::load();`. So I was kinda bummed and surprised that the compiler didn't help me notice that rather humongous change in semantics. Hence the question. – OldPeculier Aug 15 '12 at 21:15
  • there is actually already a similar question on SO [here](http://stackoverflow.com/questions/325555/c-static-member-method-call-on-class-instance ) with a some good information – Kreg Aug 15 '12 at 21:04
  • 1
    Hm, same information as the first answer here. But I don't consider "because the bible says so" an adequate answer. – OldPeculier Aug 15 '12 at 21:07
  • 2
    Yea I don't consider a bible a good reference (its based on belief). But an answer based on "The Standard" is the ultimate goal of this site. The site is not designed to answer question like "why" is it like that (as that would be pure speculation and thus belief not a fact). We want facts not beliefs on this site. – Martin York Aug 15 '12 at 22:28
  • You might have a class with instances with properties size, colour, defaultsize, and defaultcolour. Except that the defaultsize and defaultcolour doesn't actually need an object because it's the same for all objects. You can make defaultsize and defaultcolour class methods, but still call them like an instance method. – gnasher729 May 03 '14 at 01:14

5 Answers5

20

In The Design and Evolution of C++ at page 288, Bjarne Stroustrup mentions that in the days before static member functions, programmers used hacks like ((X*)0)->f() to call member functions that didn't need an object. My guess is that when static member functions were added to the language, access through -> was allowed so that programmers with code like that could change f to static without having to hunt down and change every use of it.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
10

Presumably so you can call it in places where you may not know the class type of something but the compiler does.

Say I had a bunch of classes that each has a static member that returned the class name:

class Foo
{
    static const char* ClassName() { return "Foo"; }
};

class Bar
{
    static const char* ClassName() { return "Bar"; }
};

Then all over my code I could do things like:

Foo foo;

printf( "This is a %s\n", foo.ClassName() );    

Without having to worry about knowing the class of my objects all the time. This would be very convenient when writing templates for example.

Rafael Baptista
  • 11,181
  • 5
  • 39
  • 59
  • This answer begins to hold some attraction. And yet I don't think that in practice this would ever be of benefit. (Maybe you can think of a case.) I would think that templates, as you mention, would be the best use case. So to adjust your second code block: `template void caller( T& t ) { printf( "This is a %s\n", t.ClassName(); }` would work. But then, so would `printf( "This is a %s\n", T::ClassName() );` And when would the latter ever be confusing, much less inaccessible, when the former isn't? – OldPeculier Aug 15 '12 at 21:08
  • 2
    @OldPeculier: Consider a case where you change the above code so that `Foo foo;` is now `Bar foo;`. That is the only change you need to make if you used `foo.ClassName()`. If you have used `Foo::ClassName()` you need to remember to make 2 changes. Fine with small numbers of changes. Same goes for `sizeof foo` vs `sizeof Foo`, typedefs and many other declarations where you only want to make the declaration once. – tinman Aug 15 '12 at 21:21
  • 3
    @OldPeculier, consider your template example when `Foo::ClassName` is static but `Bar::ClassName` isn't static, indeed it might be virtual. – Mark Ransom Aug 15 '12 at 21:30
  • @tinman Good point. Seems at least somewhat useful. – OldPeculier Aug 15 '12 at 21:31
  • @MarkRansom Now _that's_ interesting. Yes, I could see that being sincerely useful. It's the most compelling rationale I've heard so far (IMO). – OldPeculier Aug 15 '12 at 21:32
  • 3
    @OldPeculier: Another case is when the type was unobtainable, such as the return type from a function, since prior to C11's `decltype`, obtaining the return type was not supported by the language. It allows, `add(a,b).ClassName()` instead of having to write boilerplate code with a template function whose only purpose is to deduce the return type and invoke the static `ClassName` method on the type: `getClassName((add(a,b))`. – Tanner Sansbury Aug 15 '12 at 21:43
  • This answer should focus on the template aspect. Indeed, the example as given, where both methods are static, could simply be replaced with a call to `foo::ClassName()` and then something like `using foo = Foo;` at the top and work in the same way. The usefulness of is really when calling a method on a template parameter, where you don't know if the method will be static or not. – BeeOnRope Nov 16 '17 at 00:24
7

It's like this because the standard says that's how it works. n3290 § 9.4 states:

A static member s of class X may be referred to using the qualified-id expression X::s; it is not necessary to use the class member access syntax (5.2.5) to refer to a static member. A static member may be referred to using the class member access syntax, in which case the object expression is evaluated. [ Example:

struct process { 
  static void reschedule(); 
}; 

process& g();

void f() { 
  process::reschedule(); // OK: no object necessary
  g().reschedule(); // g() is called 
} 

end example ]

Flexo
  • 87,323
  • 22
  • 191
  • 272
  • But this doesn't answer the question as to "why" the standard is written this way. – Rafael Baptista Aug 15 '12 at 21:04
  • 4
    @RafaelBaptista - to answer that would be speculation. My suspicion is that "is this a static function" is considered an implementation detail, irrelevant to people who use it. – Flexo Aug 15 '12 at 21:04
  • 2
    Ooooh-kay. But "because the bible said so," isn't an adequate answer in cases of software engineering. Why does the Bible say so? Why did the standards committee see this as a desirable thing? It seems intrinsically confusing, since the object is ignored (apart from its type) and the rule changes the meaning of . and ->. – OldPeculier Aug 15 '12 at 21:05
  • I suggest an answer. Flexo's comment is another good reason. – Rafael Baptista Aug 15 '12 at 21:06
  • 3
    @OldPeculier I think the only way to "correctly" address these questions on SO **is** to use the specification. C++ is C++ because it is. There needs be no other reason. Of course, a reference detailing why the language was *designed* as such would give the "why?", but such *rationale* information often seems lost .. perhaps there is a quote buried in some paper or interview manuscript somewhere. –  Aug 15 '12 at 21:08
  • 1
    The answer for "why" is perhaps because this is a safe extension, which adds a bit of flexibility without danger of breaking things up. – Roman R. Aug 15 '12 at 21:11
  • With a base class which is being used as a dependent name you can call `this->foo();` as an alternative to `Base::foo();` which may or may not be clearer. – Flexo Aug 15 '12 at 21:11
  • 3
    There is no *Rationale* for the C++ standard ([unlike for C99](http://www.open-std.org/JTC1/SC22/WG14/www/C99RationaleV5.10.pdf)), so specific sections of the standard are generally not explained. – Bo Persson Aug 15 '12 at 21:13
  • @RomanR. On the other hand, this feature is frowned upon in Java and C# (in the case of `myThread.Sleep` in particular, where it sleeps the *current* thread and not "myThread", as it is really invoking the `Thread.Sleep` method) .. –  Aug 15 '12 at 21:14
  • 1
    I guess I feel more connected to the C++ language designers and gurus than some of you do. I find that I generally grok and like the decisions they make (even when they're painful—they generally make good sense). I feel some capability of influencing their decisions. And in general I like to know the reason behind the tools I use being the way they are—especially when the reasons are surprising or non-obvious. I really don't buy this, "Oh, the standard is inscrutable; let us merely obey its Holy Ways," approach. No offense—I'm exaggerating the philosophy I'm sensing here. – OldPeculier Aug 15 '12 at 21:21
7

From The Evolution of C++ (pdf), section 8. Static Member Functions:

...It was also observed that nonportable code, such as

    ((x*)0)->f();

was used to simulate static member functions.

So my guess is (based on the pattern of rationale for almost every other weird syntactical thing) they allowed invoking a static member function when you just had the type to provide backwards compatibility with an established but broken idiom.

MSN
  • 53,214
  • 7
  • 75
  • 105
1

If you don't subscribe to the "because the standard says so" school of causality, I also suggest that static methods are old enough to come from a time when people actually worried about the extra overhead from passing the this argument to a function call, so making pure functions "static" as an optimization was probably all the rage in 1985.

themel
  • 8,825
  • 2
  • 32
  • 31