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() 
{
}

Reply via email to