23.1. Hidden Costs
Let's look at another class, String:
class String
{
public:
typedef std::string const &value_type;
public:
explicit String(std::string const &value);
explicit String(char const *value);
public:
std::string const &GetValue() const;
. . .
We can use this as we did Double, with the current definition of Wrapper:
typedef Wrapper<String> String_t;
std::string ss("A std::string instance");
String_t s1("A c-style string"); // Ok
String_t s2(ss); // Ok, but . . .
Both the constructors compile, and the code works as expected, but there is a hidden gotcha. The construction of s2 results in the creation of two copies of the string ss, where we only expect and want one. The extra copy is created because compilers instantiate a template based on the types of the arguments expressed to it, without any consideration as to how they might be used within it. Thus, the construction of s2 is actually done by a constructor equivalent to the following:
Wrapper<String>::Wrapper(String s)
: m_value(s)
{}
Imperfection:
The C++ template mechanism instantiates a template based on the arguments to it, and takes no account of how, or in what form, those arguments are subsequently used within the templates. This can lead to the generation of inefficient and/or erroneous code since temporary instances of class type may be created during the forwarding of arguments through the template. |
|