没有像过度工程这样的工程。
在这种情况下,我创建了一个类型string_builder::op<?>
,该类型可以合理有效地收集一堆要连接的字符串,并在转换为std::string
收益时这样做。
它存储提供的任何临时 s 的副本std::string
,以及对寿命更长的 s 的引用,作为一种偏执狂。
它最终减少到:
std::string retval;
retval.reserve(the right amount);
retval+=perfect forwarded first string
...
retval+=perfect forwarded last string
return retval;
但它用大量的语法糖来包装它。
namespace string_builder {
template<class String, class=std::enable_if_t< std::is_same< String, std::string >::value >>
std::size_t get_size( String const& s ) { return s.size(); }
template<std::size_t N>
constexpr std::size_t get_size( const char(&)[N] ) { return N; }
template<std::size_t N>
constexpr std::size_t get_size( char(&)[N] ) { return N; }
std::size_t get_size( const char* s ) { return std::strlen(s); }
template<class Indexes, class...Ss>
struct op;
struct tuple_tag {};
template<size_t... Is, class... Ss>
struct op<std::integer_sequence<size_t, Is...>, Ss...> {
op() = default;
op(op const&) = delete;
op(op&&) = default;
std::tuple<Ss...> data;
template<class... Tuples>
op( tuple_tag, Tuples&&... ts ): data( std::tuple_cat( std::forward<Tuples>(ts)... ) ) {}
std::size_t size() const {
std::size_t retval = 0;
int unused[] = {((retval+=get_size(std::get<Is>(data))), 0)..., 0};
(void)unused;
return retval;
}
operator std::string() && {
std::string retval;
retval.reserve( size()+1 );
int unused[] = {((retval+=std::forward<Ss>(std::get<Is>(data))), 0)..., 0};
(void)unused;
return retval;
}
template<class S0>
op<std::integer_sequence<size_t, Is..., sizeof...(Is)>, Ss..., S0>
operator+(S0&&s0)&& {
return { tuple_tag{}, std::move(data), std::forward_as_tuple( std::forward<S0>(s0) ) };
}
auto operator()()&& {return std::move(*this);}
template<class T0, class...Ts>
auto operator()(T0&&t0, Ts&&... ts)&&{
return (std::move(*this)+std::forward<T0>(t0))(std::forward<Ts>(ts)...);
}
};
}
string_builder::op< std::integer_sequence<std::size_t> >
string_build() { return {}; }
template<class... Strings>
auto
string_build(Strings&&...strings) {
return string_build()(std::forward<Strings>(strings)...);
}
现在我们得到:
std::string Concatenate(const std::string& s1,
const std::string& s2,
const std::string& s3,
const std::string& s4,
const std::string& s5)
{
return string_build() + s1 + s2 + s3 + s4 + s5;
}
或更通用和有效的:
template<class... Strings>
std::string Concatenate(Strings&&...strings)
{
return string_build(std::forward<Strings>(strings)...);
}
有无关的动作,但没有无关的分配。它适用于 raw"strings"
没有额外的分配。
活生生的例子