Tom Lachecki

(Tomalak Geret'kal)


C++ Variable Initialisation: How To Do It Properly

There are two main ways to declare and initialise variables with automatic or static storage in C++: with `='; and with parentheses.

Some C++ programmers prefer to use copyconstructor-style initialisation syntax for clarity because it is the copy constructor that's invoked in both cases, not the assignment operator as one might expect from the use of the equals sign.

But I think clarity is the complete opposite of what you get. It can cause problems where you do not expect them, leading to complicated diagnostics:

Although the linker error makes sense if you know that you have declared a function and never defined it, the diagnostic does not really help you fix the problem if you did not see this coming and expected x to be equal to 0.

"Wait, what. I've declared a function?"

Yep! The only difference between a function declaration int(T) and a temporary construction int(x) is that T is a type whereas x is an expression. So:

In the last case above, because the int() has nothing inside the parentheses it can be parsed as either, and function declaration is always assumed in C++ where it's possible.

There are plenty of cases where a type simply cannot be in a situation like this, and in the other cases you can hack around it by forcing the declaration to become an expression with extra parentheses:

Still, even if just because of this, I avoid "int x(3);" and instead prefer good-old equals-syntax which, although it doesn't actually invoke the assignment operator, at least does what it says it does: it declares and initialises an integer variable called x, equal to 3:

Stick to this syntax and you won't have any trouble; problem solved. 🙂

Further reading

Tags: ,
Permalink | No Comments  
C++ Lookup And Template Oddities

Non-type Lookup

During a discussion about the validity of A::A (and the fact that you cannot pass explicit template parameters to a template constructor) it came up that although non-types are ignored in the lookup for a base-clause, the same is not true for typedef… even though clearly only types are to be expected in either case.

Observe:

But:

Stupid language.

Constructor template parameters

This discussion had come about from a debate over the technique displayed in the following example ("BARK" is a third-party preprocessor macro that outputs the signature of the function that it's in):

Using the template constructor in this way is valid because the template parameter is deduced to be int from the function argument 3.

Now, let's look at the case where the class itself is the template. The class's template parameters cannot be deduced from arguments to its constructor, alas (at least not until C++0x):

However you can, of course, specify the template class parameter explicitly:

You'd expect that specifying a template parameter explicitly is always valid, and that only when trying to rely on implicit deduction might things get a bit hairy.

But you actually can't specify a template constructor parameter explicitly at all. The interesting thing here is in GCC's diagnostic, which isn't quite what you might expect:

What is happening here is that any class A is brought into its own scope. So as a type, A::A is equivalent to A, as is A::A::A and A::A::A::A::A::A::A. GCC parses A::A<int>() as A&lt;int&gt;(), then attempts to construct a temporary of type A&lt;int&gt;, but class A isn't itself a template so this fails.

A lengthy debate ensued and it was concluded that GCC is erroneous here. Comeau in fact agrees with our ultimate interpretation of the standard, and allows this by correctly parsing A::A<int> as the constructor A&lt;int&gt; of class A… although it's still not possible to directly invoke the constructor.

I'm actually still not 100% clear on this, so please jump on the wagon in the post comments if you can shed some more light on the issue.

Tags: ,
Permalink | No Comments  
A Question On Indirect Constness

You may have used code such as [1]:

Here we obtain a pointer b to the same object that a points to, but we say that it is now immutable (through b, at least).

However, C++ does not allow a programmer to add constness more than one layer of indirection away [2]:

Of course, you can still make the pointer itself const [3]:

So it turns out that you can add constness to the pointer [3], or to the immediate pointee [1], but you can't add constness to the pointee of the pointee [2].

The question is: why not? How does adding constness violate const-correctness? Answers on a postcard, please.


Update: As Kniht correctly pointed out in the comments (and, in fact, as the standard itself explains in a note), this is invalid for very good reason:

Tags: ,
Permalink | [2] Comments