A
Angus Leeming
Hello,
Could someone explain to me why the Standard conveners chose to typedef
std::string rather than derive it from std::basic_string<char, ...>?
The result of course is that it is effectively impossible to forward declare
std::string. (Yes I am aware that some libraries have a string_fwd.h header,
but this is not portable.)
That said, is there any real reason why I can't derive an otherwise empty
String class from std::string? Will there be any hidden surprises? As I
understand it, the fact that String has no member variables of its own means
that the non-virtual std::string destructor is not an issue.
Nonetheless, I'm wary because I have heard so much about 'std::string is not
designed to be derived from'. Nobody has explained the rationale behind such
a statement to me however.
Below is sample code that appears to work just fine.
Best regards,
Angus
#ifndef FWD_DECLARABLE_STRING_H
#define FWD_DECLARABLE_STRING_H
#include <string>
struct String : public std::string {
typedef std::string base_type;
String() {}
String(base_type const & other)
: base_type(other) {}
explicit String(allocator_type const & al)
: base_type(al) {}
String(String const & other, size_type off, size_type count = npos)
: base_type(other, off, count) {}
String(String const & other, size_type off, size_type count,
allocator_type const & al)
: base_type(other, off, count, al) {}
String(value_type const * ptr, size_type count)
: base_type(ptr, count) {}
String(value_type const * ptr, size_type count, allocator_type const & al)
: base_type(ptr, count, al) {}
String(value_type const * ptr)
: base_type(ptr) {}
String(value_type const * ptr, allocator_type const & al)
: base_type(ptr, al) {}
String(size_type count, value_type ch)
: base_type(count, ch) {}
String(size_type count, value_type ch, allocator_type const & al)
: base_type(count, ch, al) {}
template <typename InIt>
String(InIt first, InIt last)
: base_type(first, last) {}
template <typename InIt>
String(InIt first, InIt last, allocator_type const & al)
: base_type(first, last, al) {}
String & operator=(value_type const * ptr)
{ base_type:perator=(ptr); }
String & operator=(value_type ch)
{ base_type:perator=(ch); }
};
#endif // FWD_DECLARABLE_STRING_H
#include <iostream>
int main()
{
String const angus = "angus";
std::string const jmarc = "jmarc";
String test1;
std::string test2;
// Assign a std::string to a String
test1 = jmarc;
std::cout << "test1 " << test1 << std::endl;
// Assign a String to a std::string
test2 = angus;
std::cout << "test2 " << test2 << std::endl;
// Assign a String to a String (implicit assignment operator)
test1 = angus;
std::cout << "test1 " << test1 << std::endl;
// Implicit copy c-tor;
String const test3 = angus;
std::cout << "test3 " << test3 << std::endl;
// Copy construct a std::string from a String
std::string test4 = angus;
std::cout << "test4 " << test4 << std::endl;
return 0;
}
Could someone explain to me why the Standard conveners chose to typedef
std::string rather than derive it from std::basic_string<char, ...>?
The result of course is that it is effectively impossible to forward declare
std::string. (Yes I am aware that some libraries have a string_fwd.h header,
but this is not portable.)
That said, is there any real reason why I can't derive an otherwise empty
String class from std::string? Will there be any hidden surprises? As I
understand it, the fact that String has no member variables of its own means
that the non-virtual std::string destructor is not an issue.
Nonetheless, I'm wary because I have heard so much about 'std::string is not
designed to be derived from'. Nobody has explained the rationale behind such
a statement to me however.
Below is sample code that appears to work just fine.
Best regards,
Angus
#ifndef FWD_DECLARABLE_STRING_H
#define FWD_DECLARABLE_STRING_H
#include <string>
struct String : public std::string {
typedef std::string base_type;
String() {}
String(base_type const & other)
: base_type(other) {}
explicit String(allocator_type const & al)
: base_type(al) {}
String(String const & other, size_type off, size_type count = npos)
: base_type(other, off, count) {}
String(String const & other, size_type off, size_type count,
allocator_type const & al)
: base_type(other, off, count, al) {}
String(value_type const * ptr, size_type count)
: base_type(ptr, count) {}
String(value_type const * ptr, size_type count, allocator_type const & al)
: base_type(ptr, count, al) {}
String(value_type const * ptr)
: base_type(ptr) {}
String(value_type const * ptr, allocator_type const & al)
: base_type(ptr, al) {}
String(size_type count, value_type ch)
: base_type(count, ch) {}
String(size_type count, value_type ch, allocator_type const & al)
: base_type(count, ch, al) {}
template <typename InIt>
String(InIt first, InIt last)
: base_type(first, last) {}
template <typename InIt>
String(InIt first, InIt last, allocator_type const & al)
: base_type(first, last, al) {}
String & operator=(value_type const * ptr)
{ base_type:perator=(ptr); }
String & operator=(value_type ch)
{ base_type:perator=(ch); }
};
#endif // FWD_DECLARABLE_STRING_H
#include <iostream>
int main()
{
String const angus = "angus";
std::string const jmarc = "jmarc";
String test1;
std::string test2;
// Assign a std::string to a String
test1 = jmarc;
std::cout << "test1 " << test1 << std::endl;
// Assign a String to a std::string
test2 = angus;
std::cout << "test2 " << test2 << std::endl;
// Assign a String to a String (implicit assignment operator)
test1 = angus;
std::cout << "test1 " << test1 << std::endl;
// Implicit copy c-tor;
String const test3 = angus;
std::cout << "test3 " << test3 << std::endl;
// Copy construct a std::string from a String
std::string test4 = angus;
std::cout << "test4 " << test4 << std::endl;
return 0;
}