QStringView: improve manual overload management

We want to prevent

  QStringView(QChar|QLatin1String|QByteArray|const char*)

from compiling as QStringView(QString(...)), so I added = delete'ed
ctors for these types to QStringView. However, that makes QStringView
participate in overload resolution for these types. Even if the
QStringView ctor will always fail to compile, the presence of these
ctors alone makes calls to functions overloaded on QString and
QStringView ambiguous:

   f(QStringView);
   f(QString);
   f(foo);        // ambiguous
   f(QChar('f'))    // ambiguous
   f(QLatin1String(foo)); // ambiguous
   f(QByteArray(foo));    // ambiguous

Fix by making the QString and QStringRef constructors templates
constrained to accept only these two types. This should also help to
move the QStringView definition to before the QString one (as soon as
we get rid of or start to ignore QString::Null), simplifying a lot of
code in qstring.h down the line.

This should also fix MSVC's accepting of two user-defined conversions
which caused static non-compile-tests to fail in the initial
QStringView patch, and which were therefore removed. This patch brings
them back.

Change-Id: I95ac38c0d31cd8c726f7e952017569d32e484413
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
bb10
Marc Mutz 2017-03-27 11:22:44 +02:00
parent e9c06b25af
commit 7180236063
2 changed files with 27 additions and 17 deletions

View File

@ -111,6 +111,9 @@ private:
template <typename T>
using if_compatible_string = typename std::enable_if<QtPrivate::IsCompatibleStdBasicString<T>::value, bool>::type;
template <typename T>
using if_compatible_qstring_like = typename std::enable_if<std::is_same<T, QString>::value || std::is_same<T, QStringRef>::value, bool>::type;
template <typename Char>
static Q_DECL_RELAXED_CONSTEXPR size_type lengthHelper(const Char *str) Q_DECL_NOTHROW
{
@ -133,20 +136,6 @@ private:
static Q_DECL_CONSTEXPR const storage_type *castHelper(const storage_type *str) Q_DECL_NOTHROW
{ return str; }
// prevent
// T t; QStringView sv(t), T \in {QChar, QLatin1String, QByteArray, const char*}
// from compiling as QStringView sv(QString(t)):
QStringView(QChar) = delete;
QStringView(QLatin1String) = delete;
QStringView(const QByteArray &) = delete;
QStringView(const char *) = delete;
#ifdef Q_OS_WIN
// prevent QStringView(Char), Char compatible, from compiling due to:
// https://connect.microsoft.com/VisualStudio/feedback/details/2256407/c-two-user-defined-conversions-incorrectly-accepted-in-implicit-conversion-sequence
template <typename Char, if_compatible_char<Char> = true>
QStringView(Char) = delete;
#endif
public:
Q_DECL_CONSTEXPR QStringView() Q_DECL_NOTHROW
: m_size(0), m_data(nullptr) {}
@ -166,10 +155,14 @@ public:
Q_DECL_CONSTEXPR QStringView(const Char *str)
: QStringView(str, str ? lengthHelper(str) : 0) {}
QStringView(const QString &str) Q_DECL_NOTHROW
: QStringView(str.isNull() ? nullptr : str.data(), size_type(str.size())) {}
QStringView(const QStringRef &str) Q_DECL_NOTHROW
#ifdef Q_QDOC
QStringView(const QString &str) Q_DECL_NOTHROW;
QStringView(const QStringRef &str) Q_DECL_NOTHROW;
#else
template <typename String, if_compatible_qstring_like<String> = true>
QStringView(const String &str) Q_DECL_NOTHROW
: QStringView(str.isNull() ? nullptr : str.data(), size_type(str.size())) {}
#endif
template <typename StdBasicString, if_compatible_string<StdBasicString> = true>
QStringView(const StdBasicString &str) Q_DECL_NOTHROW

View File

@ -43,10 +43,21 @@
template <typename T>
using CanConvert = std::is_convertible<T, QStringView>;
Q_STATIC_ASSERT(!CanConvert<QLatin1String>::value);
Q_STATIC_ASSERT(!CanConvert<const char*>::value);
Q_STATIC_ASSERT(!CanConvert<QByteArray>::value);
// QStringView qchar_does_not_compile() { return QStringView(QChar('a')); }
// QStringView qlatin1string_does_not_compile() { return QStringView(QLatin1String("a")); }
// QStringView const_char_star_does_not_compile() { return QStringView("a"); }
// QStringView qbytearray_does_not_compile() { return QStringView(QByteArray("a")); }
//
// QChar
//
Q_STATIC_ASSERT(!CanConvert<QChar>::value);
Q_STATIC_ASSERT(CanConvert< QString >::value);
Q_STATIC_ASSERT(CanConvert<const QString >::value);
Q_STATIC_ASSERT(CanConvert< QString&>::value);
@ -62,6 +73,8 @@ Q_STATIC_ASSERT(CanConvert<const QStringRef&>::value);
// ushort
//
Q_STATIC_ASSERT(!CanConvert<ushort>::value);
Q_STATIC_ASSERT(CanConvert< ushort*>::value);
Q_STATIC_ASSERT(CanConvert<const ushort*>::value);
@ -72,6 +85,8 @@ Q_STATIC_ASSERT(CanConvert<const ushort*>::value);
#if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS)
Q_STATIC_ASSERT(!CanConvert<char16_t>::value);
Q_STATIC_ASSERT(CanConvert< char16_t*>::value);
Q_STATIC_ASSERT(CanConvert<const char16_t*>::value);
@ -99,6 +114,8 @@ Q_CONSTEXPR bool CanConvertFromWCharT =
#endif
;
Q_STATIC_ASSERT(!CanConvert<wchar_t>::value);
Q_STATIC_ASSERT(CanConvert< wchar_t*>::value == CanConvertFromWCharT);
Q_STATIC_ASSERT(CanConvert<const wchar_t*>::value == CanConvertFromWCharT);