Переместить конструктор (ссылка на rvalue) в неявном преобразовании

Я обновляю проект C++ с MSVC 2008 до 2010, и из-за нового конструктора перемещения CComBSTR [CComBSTR( CComBSTR&& )] я получаю ошибку компилятора из-за неоднозначного вызова.

По сути, у нас есть класс String, очень похожий на std::wstring, который имеет оператор приведения к CComBSTR. Это похоже на следующий код:

class CString {
  public:
    // ...
    operator CComBSTR() {
      CComBSTR temp;
      /* Encoding conversion here */
      return temp;
    }
}

class CObjectConfig {
  public:
    CString GetName() const { return m_name; }

  private:
    CString m_name;
}

Теперь в некоторых местах кода мы делаем следующее:

CObjectConfig config = GetObjectConfig();
CComBSTR objectName( config.GetName() );

В VS2008 это будет работать, поскольку объект CString будет неявно преобразован в rvalue CComBSTR, а конструктор копирования CComBSTR (взяв константу CComBSTR&) будет вызываться для создания objectName.

Однако в VS2010 с C++0x компилятор выдает неоднозначную ошибку вызова, поскольку rvalue CComBSTR, кажется, подходит как для конструктора копирования, так и для конструктора перемещения.

Хотя это немного неуклюже, мое решение этой проблемы состоит в том, чтобы static_cast вызвать GetName:

CComBSTR objectName( static_cast<const CComBSTR&>( config.GetName() ) );
// or
CComBSTR objectName( static_cast<CComBSTR&&>( config.GetName() ) );

Обе строки компилируются без ошибок, но мне нужен ваш совет, является ли это незаконным, плохой практикой или неопределенным. Спасибо.


person The Stacker    schedule 15.09.2010    source источник


Ответы (1)


Мне это кажется ошибкой VC2010. Либо так, либо я неправильно смоделировал вашу ситуацию на своем компьютере (у меня нет VC2010). Вот что я делаю:

#include <iostream>

class CComBSTR
{
public:
    CComBSTR() {std::cout << "CComBSTR()\n";}
    CComBSTR(const CComBSTR&) {std::cout << "CComBSTR(const CComBSTR&)\n";}
    CComBSTR(CComBSTR&&) {std::cout << "CComBSTR(CComBSTR&&)\n";}
};

class CString {
  public:
    // ...
    operator CComBSTR() {
      CComBSTR temp;
      /* Encoding conversion here */
      return temp;
    }
};

class CObjectConfig {
  public:
    CString GetName() const { return m_name; }

  private:
    CString m_name;
};

CObjectConfig GetObjectConfig()
{
    return CObjectConfig();
}

int main()
{
    CObjectConfig config = GetObjectConfig();
    CComBSTR objectName( config.GetName() );
}

Для меня на g++-4.4 и clang (с -std=c++0x) это компилируется нормально. И он либо вызывает, либо пропускает вызов CComBSTR(CComBSTR&&). Моя рекомендация по обходу этой предполагаемой ошибки проста:

CComBSTR objectName( CComBSTR(config.GetName()) );

Это эквивалентно вашему:

CComBSTR objectName( static_cast<CComBSTR&&>( config.GetName() ) );

но не так страшно (и так же эффективно). Если вы хотите остаться со static_cast, тогда используйте приведение к CComBSTR&&, так как это, вероятно, будет более эффективным, чем построение из константного lvalue.

person Howard Hinnant    schedule 08.03.2011