RValue, разрешение шаблонов и конструкторы копирования (в Visual C ++ 2010) - PullRequest
3 голосов
/ 09 августа 2011

Я создаю простой контейнерный класс, но сталкиваюсь с некоторыми проблемами (повторная сборка из них в Visual C ++ 2010, ошибка ссылки на значение? )

#include <cassert>
#include <utility>

template<typename T0>
class MyType {
 public:
  typedef T0 value_type;

  // Default constructor
  MyType() : m_value() {
  }

  // Element constructor
  explicit MyType(const T0 &c_0) : m_value(c_0) {
  }

  template<typename S0>
  explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
  }

  // Copy constructor
  MyType(const MyType &other) : m_value(other.m_value) {
  }

  MyType(MyType &&other) : m_value(std::forward<value_type>(other.m_value)) {
  }

  // Copy constructor (with convertion)
  template<typename S0>
  MyType(const MyType<S0> &other) : m_value(other.m_value) {
  }

  template<typename S0>
  MyType(MyType<S0> &&other) : m_value(std::move(other.m_value)) {
  }

  // Assignment operators
  MyType &operator=(const MyType &other) {
    m_value = other.m_value;
    return *this;
  }

  MyType &operator=(MyType &&other) {
    m_value = std::move(other.m_value);
    return *this;
  }

  template<typename S0>
  MyType &operator=(const MyType<S0> &other) {
    m_value = other.m_value;
    return *this;
  }

  template<typename S0>
  MyType &operator=(MyType<S0> &&other) {
    m_value = std::move(other.m_value);
    return *this;
  }

  // Value functions
  value_type &value() {
    return m_value;
  }

  const value_type &value() const {
    return m_value;
  }

 private:
  template<typename S0>
  friend class MyType;

  value_type m_value;
};

int main(int argc, char **argv) {
  MyType<float>  t1(5.5f);
  MyType<double> t2(t1);

    return 0;
}

Приведенный выше код даетследующая ошибка:

1>ClCompile:
1>  BehaviorIsolation.cpp
1>behaviorisolation.cpp(18): error C2440: 'initializing' : cannot convert from 'MyType<T0>' to 'double'
1>          with
1>          [
1>              T0=float
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>          behaviorisolation.cpp(78) : see reference to function template instantiation 'MyType<T0>::MyType<MyType<float>&>(S0)' being compiled
1>          with
1>          [
1>              T0=double,
1>              S0=MyType<float> &
1>          ]
1>behaviorisolation.cpp(18): error C2439: 'MyType<T0>::m_value' : member could not be initialized
1>          with
1>          [
1>              T0=double
1>          ]
1>          behaviorisolation.cpp(73) : see declaration of 'MyType<T0>::m_value'
1>          with
1>          [
1>              T0=double
1>          ]
1>
1>Build FAILED.

Как можно исправить эту ошибку, не используя приемы, подобные описанным в связанном вопросе?

Спасибо!

Редактировать: Больше всего меня смущает, почему не назван ни один из двух специализированных конструкторов.Они подходят под вызов намного лучше.

  // Copy constructor (with convertion)
  template<typename S0>
  MyType(const MyType<S0> &other) : m_value(other.m_value) {
  }

  template<typename S0>
  MyType(MyType<S0> &&other) : m_value(std::move(other.m_value)) {
  }

Ответы [ 2 ]

2 голосов
/ 09 августа 2011

Ваш связанный вопрос уже отвечает на это.Давайте определим

typedef MyType<float>  MF;
typedef MyType<double> MD;

Когда вы говорите MD t2(t1);, вы бы хотели бы для вызова конструктора MF::MF(const MD &).Тем не менее, конструктор template <typename T> MF::MF(T&&) соответствует лучше, потому что он принимает T = MD& и, следовательно, разрешается в MF::MF(MD&), что является лучшим соответствием из-за отсутствия const.

Чтобы решить эту проблему, вы должныпо сути, избавьтесь от конструктора MF(T&&), как уже предлагал Говард.Так как в любом случае вы намерены использовать этот конструктор только для значений, мое первое предложение будет изменить подпись на MF(const T &), что уже решит вашу проблему.Другим решением было бы добавить конструктор с подписью MF(MD&) (неконстантный).Это уродливо, хотя.Наконец, вы можете вызвать конструктор explicity на сайте вызова: MD t2(MF(t1)), или MD t2(std::forward<MF>(t1)), или даже MD t2(std::move(t1)), если это вариант.

Наконец, обратите внимание, что если вы имеете дело только с примитивными членами,от явных ходов ничего не получится, так что вы можете не беспокоиться об определении всех этих конструкторов по отдельности.

2 голосов
/ 09 августа 2011

Ваш конструктор:

 template<typename S0>
  explicit MyType(S0 &&c_0)

является чрезмерно общим, и лучший способ решить вашу проблему - ограничить тип, который может быть выведен как S0. Это в основном то, что делает связанный ответ. Но, возможно, я смогу сделать его более привлекательным для вас.

Вот оно, в std :: C ++ 11:

  template<typename S0,
           class = typename std::enable_if
           <
               std::is_convertible<S0, T0>::value
           >::type>
  explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
  }

Если это слишком уродливо, вы можете подумать:

#define restrict_to(x...) class = typename std::enable_if<x>::type

...

template<typename S0, restrict_to(std::is_convertible<S0, T0>::value)>
explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
}

Обратите внимание, что это не только решит вашу проблему, но и если ваши клиенты впоследствии задают такие вопросы, как:

std::is_convertible<X, MyType<T>>::type

теперь они получат правильный ответ. Как у вас сейчас написано, вышеуказанная черта всегда отвечает истине.

...