явный конструктор копирования для ошибки генератора шаблонов - PullRequest
2 голосов
/ 21 марта 2019

При переносе старого кода из MSVS2003 в MSVS2017 и возникли проблемы.Следующий код (отрывок) отлично скомпилирован в MSVS2003 и завершается ошибкой в ​​MSVS2017:

template<typename T> class TTT
{
public:
    template<typename X, typename P1, typename P2> bool allocT( P1 p1, P2 p2 )
    {
        p = new X( p1, p2 );
        return true;
    }
    T * p;
};

struct AAA
{
    virtual ~AAA(){}
    virtual bool dup( TTT<AAA> & dst, bool param ) const = 0;   // require dup method in all derived classes
};
struct BBB : public AAA
{
    explicit BBB( const BBB & src, bool param = false );

    bool dup( TTT<AAA> & dst, bool param ) const override;
};
inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( *this, param );
}

Точное сообщение об ошибке:

1>[...]: error C2664: 'bool TTT<AAA>::allocT<BBB,BBB,bool>(P1,P2)': cannot convert argument 1 from 'const BBB' to 'BBB'
1>          with
1>          [
1>              P1=BBB,
1>              P2=bool
1>          ]
1>  [...]: note: Constructor for struct 'BBB' is declared 'explicit'

Эта ошибка исчезает, если выполнено одно из следующих действий:

  • конструктор объявлен не-explicit (как советует компилятор);
  • параметр `param 'конструктора объявлен не по умолчанию:

    explicit BBB( const BBB & src, bool param ); (хотя и остается явным);

  • вызов allocT полностью специализирован:

    return dst.allocT< BBB, const BBB &, bool >( *this, param );

Ни одно из этих решений мне не подходит:

  • Я бы не хотел удалять explicit, так как это выглядит подозрительно - похоже, компилятор пытается создать временный файл и передать его дальше;
  • удаление параметра по умолчанию эффективно предотвращает конструктор копирования-конструктора и, вероятно, генерирует определенную компилятором версию, которая позже используется для создания временного;
  • указывать все не удобнотипов параметров конструктора eacВремяЯ просто хочу передать параметры конструктору BBB.

Пытаясь понять, почему компилятор не может назначить *this в const BBB &, я создал вспомогательную функцию теста, которая явно преобразует указатель в constссылка, и это тоже не помогло:

template const T & deref( const T * ptr ) { ... } ... return dst.allocT( deref(this), param );

Несколько замечаний по исходному коду:

  • TTT является своего рода умным указателем, он не можетбыть заменены стандартными интеллектуальными указателями, поскольку они предоставляют нестандартные функции;
  • AAA и BBB на самом деле являются довольно тяжелыми классами с подсчетом ссылок, и поэтому их копирование очень неоптимально.

Это очень неожиданная проблема при переносе кода, я здесь совершенно озадачен.Кажется, что я что-то упустил в современном стандарте C ++, который мешает компилировать старый код под новые компиляторы.Я пытался найти решение здесь на SO, но не смог, извините, если это дубликат.Пожалуйста, помогите мне решить ее или хотя бы понять корни проблемы.

1 Ответ

3 голосов
/ 21 марта 2019

Вы выполняете случайную копию BBB, когда передаете ее в функцию allocT.Это вызывает ошибку компилятора.

Вы вызываете функцию распределения здесь:

inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( *this, param );
}

Поскольку ваша функция распределения принимает свои аргументы в качестве значений, они будут скопированы:

template<typename X, typename P1, typename P2> 
bool allocT( P1 p1, P2 p2 ) { ... }

Однако этонеявная (скрытая) копия, потому что вы не указываете явно копировать объект (this в вашем случае).Поскольку вы объявили свой конструктор копирования explicit, эти вызовы копирования больше не разрешены.

Чтобы исправить это, вы можете либо изменить allocT на ссылку (что я рекомендую)

template<typename X, typename P1, typename P2> bool 
allocT( P1 const& p1, P2 p2 ) { ... }

, либо явно скопировать BBB при передаче его на allocT как

inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( BBB(*this), param );
}

или (начиная с C ++ 17)

inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( static_cast<BBB>(*this), param );
}
...