I have a function which I have an overload for like such:
void process(const std::string& text)
{
}
void process(const std::vector<std::string>& manyTexts)
{
}
And I call it like:
process({"hello", "hey"});
I would expect this to resolve to the second overload, however this is apparently ambiguous. To my surprise I found this to compile:
std::string text = {"hello", "hey"};
Where the variable only contains 'hello' which doesn't seem very useful nor intuitive.
With this I have two questions:
std::string
? std::string
has a constructor:
template< class InputIt >
basic_string( InputIt first, InputIt last,
const Allocator& alloc = Allocator() );
The call
std::string text = {"hello", "hey"};
resolves to that constructor. Even though syntactically the call resolves to a valid constructor, this will lead to undefined behavior at run time since "hello"
and "hey"
are unrelated strings.
The only way to make
process({"hello", "hey"});
to resolve to the second function is to make it explicit.
process(std::vector<std::string>{"hello", "hey"});
I'd back up. Instead of fixing your problem, I'd expose a process( std::experimental::span<const std::string> )
overload.
See std::experimental::array_view
, a weaker version of which is easy to write.
Most of the work is in the long list of ctors you want:
template<class T>
struct span {
T* b = nullptr;
T* e = nullptr;
T* begin() const { return b; }
T* end() const { return e; }
bool empty() const { return begin()==end(); }
std::size_t size() const { return end()-begin(); }
T& front() const { return *begin(); }
T& back() const { return *std::prev(end()); }
// extra useful things I have added in my version:
span without_front(std::size_t N=1) const {
return {begin()+(std::min)(size(), N), end()};
}
span without_back(std::size_t N=1) const {
return {begin(), end()-(std::min)(size(), N)};
}
// ctors: span uses "pointer semantics":
span()=default;
span(span const&)=default;
span& operator=(span const&)=default;
~span()=default;
// useful ctors for making a span:
span( T* s, T* f ):b(s), e(f) {}
span( T* s, std::size_t len ):span(s, s+len) {}
using non_const_T = std::remove_const_t<T>;
template<class A>
span( std::vector<T, A>& in ):span(in.data(), in.size()) {}
template<class A>
span( std::vector<non_const_T, A> const& in ):span(in.data(), in.size()) {}
template<class Traits>
span( std::string<T, Traits>& in ):span(in.data(), in.size()) {}
template<class Traits>
span( std::string<non_const_T, Traits> const& in ):span(in.data(), in.size()) {}
template<std::size_t N>
span( std::array<T, N>& in ):span(in.data(), in.size()) {}
template<std::size_t N>
span( std::array<non_const_T, N>const& in ):span(in.data(), in.size()) {}
template<std::size_t N>
span( T(&in)[N] ):span(in.data(), in.size()) {}
span( std::initializer_list<non_const_T> in ):span(in.begin(), in.size()) {}
};
This gets rid of needless allocation in your vector
implementation.
Which makes the single-string version pointless.
void process(span<const std::string> texts)
{
for (const std::string& text:texts) {
// process one element
}
}
to call with a string, do a process({"bob"})
. For a string s
, do process({s})
. To call with a vector v, do process(v)
.
An industrial quality span
would only have the process(non_const_T)
style constructors for a const T
, but I'm lazy.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.