Herb的观点是,在已经分配了存储空间的情况下,按价值计算可能会效率低下并导致不必要的分配。但是采用byconst&
几乎是很糟糕的,就像采用原始C字符串并将其传递给函数一样,会发生不必要的分配。
您应该采取的是从字符串读取的抽象,而不是字符串本身,因为这是您所需要的。
现在,您可以执行以下操作template
:
class employee {
std::string name_;
public:
template<class T>
void set_name(T&& name) noexcept { name_ = std::forward<T>(name); }
};
这是相当有效的。然后添加一些SFINAE也许:
class employee {
std::string name_;
public:
template<class T>
std::enable_if_t<std::is_convertible<T,std::string>::value>
set_name(T&& name) noexcept { name_ = std::forward<T>(name); }
};
因此我们在接口而不是实现上会遇到错误。
这并不总是可行的,因为它需要公开公开实现。
这是string_view
类型类可以进入的地方:
template<class C>
struct string_view {
C const* b=nullptr;
C const* e=nullptr;
C const* begin() const { return b; }
C const* end() const { return e; }
C const& front() const { return *b; }
C const& back() const { return *std::prev(e); }
std::size_t size() const { return e-b; }
bool empty() const { return b==e; }
C const& operator[](std::size_t i){return b[i];}
string_view() = default;
string_view(string_view const&)=default;
string_view&operator=(string_view const&)=default;
string_view(C const* s, C const* f):b(s),e(f) {}
template<std::size_t N>
string_view(const C(&arr)[N]):string_view(arr, arr+N){}
template<std::size_t N>
string_view(std::array<C, N> const& arr):string_view(arr.data(), arr.data()+N){}
template<std::size_t N>
string_view(std::array<C const, N> const& arr):string_view(arr.data(), arr.data()+N){}
template<class... Ts>
string_view(std::basic_string<C, Ts...> const& str):string_view(str.data(), str.data()+str.size()){}
template<class... Ts>
string_view(std::vector<C, Ts...> const& vec):string_view(vec.data(), vec.data()+vec.size()){}
string_view(C const* str):string_view(str, str+len(str)) {}
private:
static std::size_t len(C const* str) {
std::size_t r = 0;
if (!str) return r;
while (*str++) {
++r;
}
return r;
}
};
可以直接从astd::string
或a构造这样的对象,"raw C string"
并且几乎无成本地存储您需要知道的内容,以便从中生成新的对象std::string
。
class employee {
std::string name_;
public:
void set_name(string_view<char> name) noexcept { name_.assign(name.begin(),name.end()); }
};
并且由于现在我们set_name
有一个固定的接口(不是完美的转发接口),因此它的实现可能不可见。
唯一的低效率是,如果您传递C风格的字符串指针,则在某种程度上不必要地遍历了它的大小两次(第一次寻找'\0'
,第二次复制它们)。另一方面,这为您的目标信息提供了必须达到的大小,因此可以预先分配而不是重新分配。
noexcept
是个神话,因为发生在被叫方的分配可能会抛出(例如,std::string
从原始字符串文字或其他构建时std::string
)。因此,主体不会抛出该异常,但是调用该函数仍可能导致抛出异常(std::bad_alloc
)