This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug libstdc++/79162] [7 Regression] [C++17] ambiguity in string assignment due to string_view overload
- From: "daniel.kruegler at googlemail dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Sat, 15 Jul 2017 20:51:17 +0000
- Subject: [Bug libstdc++/79162] [7 Regression] [C++17] ambiguity in string assignment due to string_view overload
- Auto-submitted: auto-generated
- References: <bug-79162-4@http.gcc.gnu.org/bugzilla/>
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79162
--- Comment #14 from Daniel Krügler <daniel.kruegler at googlemail dot com> ---
(In reply to Jonathan Wakely from comment #10)
> (In reply to Jonathan Wakely from comment #8)
> > Richard also says the overload shouldn't exist and is a bug, but the
> > overload has to exist, because the C++17 draft is defective.
>
> That's https://wg21.link/lwg2946
The problem here is that the current gcc implementation differs from the LWG
2949 wording. The wording suggests to use
template<class T>
basic_string& operator=(const T& t);
but the existing gcc approach uses effectively a by-value signature:
template<class T>
basic_string& operator=(T t);
In the example code, the model type llvm::cl::opt is not copy-constructible,
but the constraints accept it, because it is (a) convertible to
std::string_view (Because it inherits from std::string) and (b) it is not
convertible to char*. Now after acceptance of the constraints, the template
signature is instantiated, because it is a (slightly) better match than the
copy_assignment operator of std::string. When this has happened, the compile
error arises, because it is attempted to call the deleted (and private) copy
constructor of llvm::cl::opt.
It seems to me that it would suffice to change the signature of the constrained
assignment operator to use const T& instead of T, as suggested by the issue
resolution proposal.
Here is a reproducer for the situation:
#include <string>
template <class DataType>
class opt : public DataType
{
opt(const opt &) = delete;
opt &operator=(const opt &) = delete;
public:
opt() {}
};
int main()
{
opt<std::string> PGOTestProfileFile;
std::string ProfileFileName;
ProfileFileName = PGOTestProfileFile;
}
Here is a minimized emulation of basic_string that shows that the suggested fix
should work (Please uncomment '#define USE_FIX' below):
#include <type_traits>
#include <string>
#include <memory>
#include <cstddef>
namespace xstd {
template<class CharT, class Traits = std::char_traits<CharT>>
struct basic_string_view
{
using traits_type = Traits;
using size_type = std::size_t;
constexpr basic_string_view() noexcept
: len{0}, str{nullptr}
{}
constexpr basic_string_view(const CharT* str)
: len{str == nullptr ? 0 : traits_type::length(str)},
str{str}
{}
constexpr size_type
size() const noexcept
{ return this->len; }
constexpr const CharT*
data() const noexcept
{ return this->str; }
private:
size_type len;
const CharT* str;
};
template<class CharT, class Traits = std::char_traits<CharT>>
struct basic_string
{
private:
using sv_type = basic_string_view<CharT, Traits>;
template<typename T, typename Res>
using If_sv = std::enable_if_t<
std::__and_<
std::is_convertible<const T&, sv_type>,
std::__not_<std::is_convertible<const T&, const CharT*>>
>::value,
Res>;
public:
using size_type = std::size_t;
static const size_type npos = static_cast<size_type>(-1);
basic_string&
operator=(const basic_string& str);
basic_string&
operator=(const CharT* s);
basic_string&
operator=(CharT c);
basic_string&
operator=(std::initializer_list<CharT>);
basic_string&
operator=(basic_string&& str);
//#define USE_FIX
#ifndef USE_FIX
template<typename T>
If_sv<T, basic_string&>
operator=(T sv);
#else
template<typename T>
If_sv<T, basic_string&>
operator=(const T& sv);
#endif
operator sv_type() const noexcept;
};
using string = basic_string<char>;
}
namespace llvm {
namespace cl {
template <class DataType>
class opt_storage : public DataType
{
};
template <class DataType>
class opt : public opt_storage<DataType>
{
opt(const opt &) = delete;
opt &operator=(const opt &) = delete;
public:
opt();
};
}
}
using namespace llvm;
cl::opt<xstd::string>
PGOTestProfileFile;
namespace {
class PGOInstrumentationUseLegacyPass {
PGOInstrumentationUseLegacyPass(xstd::string)
{
ProfileFileName = PGOTestProfileFile;
}
xstd::string ProfileFileName;
};
}
int main()
{
}