August 31, 2006 Archives

2006-08-31

special treatment of default constructors

C++ inheritence from C is both a blessing and a curse. Here it is an example for the latter: The part of C is that functions can be declared everywhere. The part of C++ is, that constructors are (almost) treated like functions. This leads to ambguity.

class Foo { };

int main()
{
    Foo foo();
}
What is foo in this example? An object of type Foo or a forward declared function which returns a Foo object and has no arguments?

The standard defines the latter. Unfortunately I have no clue what function declerations inside other functions are good for. But nevertheless the possibility to do so was inherited from C. To avoid this ambiguity the C++ standard defines that in order to instantiate an object of type Foo one needs to omit the parantheses.

 
int main() 
{ 
    Foo foo; 
} 

To make things worse this special rule applies only for the special case where this ambiguity occurs and not for all cases where default constructors are used. For instance the following is a syntax error:

throw Foo;

If somebody knows some background to this ugly exception and contradiction please mail me. I would apprechiate if I could add some explanations to this facts.


Posted by Alexander Bernauer | Permanent Link | Categories: C++

2006-08-31

unusable feature

Templates are a powerfull feature of the C++ programing language. But they where introduced later into the language which leaded to some problems in conjunction with other parts of the language. Here is one of them:

A compiler can ascertain type and non-type template arguments from a call if the function arguments identify the template arguments unequivocaly. (See Bjarne Stroustrup, the C++ programing language, 4th edition, chapter 13.3.1). If the template arguments can not be ascertained they have to be provided. For example

template <class T>
void foo(T t)
{
    cout << typeid(T).name() << endl;
}

template <class T>
void bar()
{
    cout << typeid(T).name() << endl;
}

int main()
{
    foo(3);
    bar<int>();
}   
prints twice "i", which is the typename of integers.

Combining template functions with classes leads to member templates where the same things count. Even constructors can be templates.

class Foo {
public:
    template<class T> 
    Foo(T t)
    {
        cout << typeid(T).name() << endl;
    }
};

int main()
{
    Foo f(3);    
}
This example again prints "i". So what's the problem then?

Here it is:

class Bar {
public:
    template<class T> 
    Bar()
    {
        cout << typeid(T).name() << endl;
    }
};
If you find a way how to instantiate an object of type Bar you are my C++ guru. AFAICS there is no way. This is because constructors are only almost like functions. Using the typename as an implicit function call means one can not provide template arguments to the function because the syntax allows only template parameters for the type.

Here goes some tries and the output of the g++ 3.3.5:

Bar<int> b();  // non-template type `Bar' used as a template
Bar b<int>();  // template-id `b<int>' in declaration of primary template

AFAICS this case was just forgotten when the standard was written. At least I could not find any hints on this anywhere. If you know more about this topic please mail me.


Posted by Alexander Bernauer | Permanent Link | Categories: C++

2006-08-31

missing functionality

The SGI extensions to the STL defines a hash_map being an efficient map without allowing ordered access to the elements and thus being "useful for dictionaries".

So far so good. The big surprise is that hash_map cannot be used with string as key out of the box, because the SGI extension does not define a proper hash function. From http://www.sgi.com/tech/stl/hash.html

The hash template is only defined for template arguments of type char*, const char*, crope, wrope, and the built-in integral types.

So, if you want to use string as the key for a hash_map you have to provide a template specialization like this (supposing you are using the GNU implementation of the STL)

namespace __gnu_cxx {
    template<> struct hash<std::string> {
    public:
        size_t operator()(const std::string& s) const
        {
            return h(s.c_str());
        }
    private:
        __gnu_cxx::hash<const char*> h;
    };
}

I have no idea why this spezialication is not included from the begining. Afterall dictionaries are often used to store strings. If someone knows some background please mail me. My conjecture is that SGI wanted rope to be prefered over string. First because there are hash functions for ropes and second because of http://www.sgi.com/tech/stl/basic_string.html:

Note that the C++ standard does not specify the complexity of basic_string operations. In this implementation, basic_string has performance characteristics very similar to those of vector: access to a single character is O(1), while copy and concatenation are O(N). By contrast, rope has very different performance characteristics: most rope operations have logarithmic complexity.

Thanks to Andreas Bernauer for reporting this.


Posted by Alexander Bernauer | Permanent Link | Categories: C++