August 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.
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.
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 hashtemplate 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.
2006-08-23
copy_n loses input tokens
Dealing with input streams it is impossible to obtain an iterator which represents the n'th element without consuming
the previous n-1 elements. But such an iterator would be needed as end marker for copy. This is where
copy_n helps out.
From http://www.sgi.com/tech/stl/copy_n.html
Copy_n is almost, but not quite, redundant. If first [the first parameter] is an input iterator, as opposed to a forward iterator, then the copy_n operation can't be expressed in terms of copy.
Sounds good, but isn't. Using copy_n is cumbersome and error-prone. If your use copy_n the way one IMO expects it should be use you end in losing elements.
#include <ext/algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
using namespace std;
using namespace __gnu_cxx;
int main()
{
istringstream in("This is a test string for copy_n! Bye Bye.");
vector<string> vec;
// copy four words to the vector
copy_n(istream_iterator<string>(in), 4, back_inserter(vec));
/** now we lost one word! **/
// append a string to the vector
vec.push_back("oops");
// append "the rest" of the sentence to the vector
copy(istream_iterator<string>(in), istream_iterator<string>(), back_inserter(vec));
// output the vector
copy(vec.begin(), vec.end(), ostream_iterator<string>(cout, " "));
return 0;
}
The output of the program is "This is a test oops for copy_n! Bye Bye."
The reason for the missing word is that istream iterators consume elements from the underlaying input stream when
they are constructed and every time they are incremented. So the following correct impelementation of copy_n results in count+1 elements being consumed from the input stream.
template <class InputIterator, class Size, class OutputIterator>
OutputIterator copy_n(InputIterator first, Size count,
OutputIterator result)
{
for ( ; count > 0; --count) {
*result= *first;
++first;
++result;
}
return result;
}
The element which is consumed by the last call of operator++ is lost. This turns the SGI version of copy_n unusable. I guess thats why the GNU changed the API to return pair<InputIterator, OutputIterator>. So one can rescue the lost element like this:
// [...]
pair <istream_iterator <string>, back_insert_iterator <vector <string> > > tmp_it
= copy_n(istream_iterator <string> (in), 4, back_inserter (vec));
vec.push_back(*tmp_it.first);
// [...]
What a hack! But I don't know any better solution either. The problem arises from the fact that input streams do not behave like other sequences but have side effects when elements are consumed. But nevertheless they are forced to be accessible through a common iterator interface. This must cause problems somewhere...
This issue was reported by Marco Baur. Thanks a lot.