Обновление C ++ до VS2017: ошибка C2668 о неоднозначном вызове - PullRequest
0 голосов
/ 18 мая 2018

Я обновляю старую библиотеку с VS2010 до VS2017.Я получил ошибку, которую мне удалось исправить, но я не понимаю, почему исправление работает.

Ниже я сделал небольшой тест, который воспроизводит ошибку в VS2017.Однако, если вы запустите это в VS2010 или раскомментируете конструктор копирования в классе Date, он будет работать нормально.

Я получаю ошибку:

error.cpp(115): error C2668: 'Date::Date': ambiguous call to overloaded function  
error.cpp(22): note: could be 'Date::Date(Date &&)'  
error.cpp(22): note: or       'Date::Date(const Date &)'  
error.cpp(19): note: or       'Date::Date(std::string)'  
error.cpp(18): note: or       'Date::Date(int)'  
error.cpp(115): note: while trying to match the argument list '(CVariant)'  

Код

#include "stdafx.h"  
#include <string>  
#include <memory>  

class Date  
{
public:
    Date() { date = 19000101; }  

    // Copy constructor
    // The code will not compile in VS2017 if this constructor is not there,
    // But it compiles fine in VS2010
    /*Date(const Date & dt) {
        date = dt.date;
    }*/
    explicit Date(int yyyymmdd) { date = yyyymmdd; }  
    explicit Date(std::string isodate) { date = 19000101; } // Silly  constructor, just for this example  
private:
    int date;
};
enum cvtype {
    mInt,
    mDate,
    mNone
};

class CVariant
{
public:
    CVariant() {}

    // Copy constructor
    CVariant(const CVariant& variant) {
        copy_CVariant(variant);
    }

    // Copy assignment
    CVariant& operator=(const CVariant& variant) {
        copy_CVariant(variant);
        return *this;
    }

    void copy_CVariant(const CVariant& variant)
    {
        switch (variant._type)
        {
        case mInt:
            operator=(variant.value._Int);
            break;
        case mDate:
            operator=(*variant.value.pDate);
            break;
        default:
            clear();
            break;
        }
    }

    // Other constructors
    CVariant(const Date& date_value) : _type(mNone) { operator=(date_value);}  
    CVariant(int int_value) : _type(mNone) { operator=(int_value); }

    // casting
    operator int() const {
        if (_type == mInt) return value._Int;
        else return 0;
    }
    operator Date() const {
        if (_type == mDate) return *value.pDate;
        return Date();
    }

    // Assignment
    CVariant& operator=(int int_value) {
        clear();
        _type = mInt;
        value._Int = int_value;
        return *this;
    }
    CVariant& operator=(const Date& date_value) {
        clear();
        _type = mDate;
        value.pDate = new Date(date_value);
        return *this;
    }

private:
    void clear()
    {
        if (_type == mDate)
            delete value.pDate;
    }

    union VarValue
    {
        int _Int;
        Date* pDate;
    } value;

    cvtype _type;

};

int main()
{
    Date t(20170516);
    int i(10);
    CVariant cvt(t); 
    CVariant cvi(i); 
    // The following line only works in VS2017 if  
    // you uncomment the copy constructor in the Date class   
    // This works fine in VS2010 no matter what
    Date t1(cvt); 
    // This works 
    Date t2 = cvt;
    Date t3 = cvi;
    int i1 = cvt;
    int i2 = cvi;
    Date t4(cvt.operator Date());
    Date t5 = cvt.operator Date();
    int i3 = cvi;
    return 0;
}

Мне кажется, что я понимаю ошибку: когда я пытаюсь создать Date из CVariant,возможно несколько преобразований, каждое в разные конструкторы Date, поэтому вызов неоднозначен.

Но почему добавление конструктора копирования решает эту проблему?

Большое спасибо за помощь!

PS Мне известно, что используются неявные операторные преобразования, особенно в арифметических типах.не очень хорошая идея, но мой первый приоритет - просто собрать эту старую библиотеку.

1 Ответ

0 голосов
/ 18 мая 2018

Проблема

И версия, и версия без явного конструктора копирования не являются допустимым кодом C ++ из-за неоднозначных вызовов.

Просто случается, что компилятор MSVC что-то делает ""Волшебный" и нестандартный для его компиляции (общая тема с MSVC).Если вы попробуете любой из других основных компиляторов (gcc, clang, icc, смотрите живой пример здесь ), они все не смогут его скомпилировать.Я бы не стал полагаться на такой неоднозначный код, даже если он «работает», так как он может (и, вероятно, перестанет) работать с другой версией компилятора или другим компилятором.последовательности неявных преобразований: он всегда пытается выполнить их минимальное количество и, самое большее, одно пользовательское преобразование.Стандарт описывает этот процесс более подробно в [class.conv] .

В вашем случае при вызове Date t1(cvt); существует два способа разрешить вызовдля каждого из них требуется ровно одно пользовательское преобразование (и никаких других преобразований):

  1. Преобразование из CVariant в int (CVariant::operator int()) с последующим вызовом Date::Date(int).
  2. Преобразование из CVariant в Date (CVariant::operator Date()) с последующим вызовом (неявного) конструктора копирования Date::Date(const Date &).

Решение

Существует несколько способов решения этой проблемы:

  1. Добавьте ключевое слово explicit к одному из преобразований CVariant, чтобы оно больше не участвовало в неявных преобразованиях.
  2. Укажите, какое преобразование вы хотите на сайте вызова (например, Date t1(static_cast<Date>(cvt) для использования CVariant::operator Date()).
  3. Добавьте конструктор преобразования из CVariant в Date (Date::Date(const CVariant &)), что сделает этот конструктор не требующим преобразований, поэтому компилятор предпочтет этот вместо двух других.

Как реализовать вариант 3

См. Полный пример здесь .

Короче говоря, вам нужно сделать следующее:

  • forward-Declare CVariant, поэтому его имя доступно при создании конструктора преобразования в Date
  • . Добавьте объявление конструктора в Date
  • , определите конструктор после того, как CVariant имеетбыло определено, так что вы можете использовать преобразование из Cvariant в Date при реализации конструктора

Вот соответствующие изменения в коде:

class CVariant;

class Date  
{
public:
    // [...]
    explicit Date(const CVariant &cvt);
    // [...]
};

class CVariant
{
    // [...]
};


Date::Date(const CVariant &cvt) : Date(cvt.operator Date()) {}
...