проблема с шаблоном operator = с параметром const - PullRequest
0 голосов
/ 03 апреля 2020

Это на c ++, с использованием Visual Studio 2019 (не пробовал другие компиляторы). Я хочу добавить шаблонный метод operator =. Если параметр не является константным, он работает нормально. Но если параметр const, даже если я создаю версию с параметром const, он не вызывается. Вместо этого он выполняет простое поверхностное копирование.

Если я использую именованную функцию вместо оператора, она работает как положено. Точно так же, если это не шаблонизировано, оператор вызывается как ожидалось. Кажется, проблема в комбо.

Вот пример, демонстрирующий проблему.

class CTest
{
public:
    int x{};

    CTest() = default;
    CTest(int value) : x(value) {}

    // non-const operator=
    template<class SrcType>void operator=(SrcType& src)
    {
        x = src.x;
    }

    // const operator=
    template<class SrcType>void operator=(const SrcType& src)
    {
        x = src.x;
    }
};

int main()
{
    CTest   nonConstSrc{ 3 };
    const CTest constSrc{ 5 };
    CTest result;
    result = nonConstSrc;   // correctly calls non-const operator=
    result = constSrc;      // ? shallow copy, not calling const operator=

    return 0;
}

Есть идеи, как заставить его использовать мою перегруженную функцию? Спасибо.

Ответы [ 2 ]

1 голос
/ 03 апреля 2020

Ваш шаблон функции const не вызывается, потому что компилятор сгенерировал оператор копирования по умолчанию по умолчанию, который имеет подпись operator=(const CTest&). Когда компилятор должен выбирать между не шаблонной функцией и шаблонной (когда оба имеют одинаковое совпадение), предпочтение отдается первому. Вот почему ваш шаблонный метод не вызывается.

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

CTest& operator=(const volatile CTest&) = delete;

выше, отключите normal operator = , volatile здесь важно, без этого компилятор будет жаловаться, что operator= отключен. Добавляя volatile , вы просто делаете эту версию шаблона более подходящей, чем volatile one.

Остальное не изменяется:

 template<class SrcType> 
 void operator=(SrcType& src)
 {
     x = src.x;
     puts("boom1");
 }

 template<class SrcType>
 void operator=(const SrcType& src) {
     x = src.x;
     puts("boom2");
 }

Демо

0 голосов
/ 03 апреля 2020

Если у класса нет объявленного назначения копирования, компилятор сгенерирует его неявно.

Ваш класс не имеет оператора назначения копирования, у него есть только оператор назначения шаблона.

При вызове с объектом const неявно объявленный оператор присваивания лучше подходит при разрешении перегрузки.

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

#include <iostream>

class CTest
{
public:
    int x{};

    CTest() = default;
    CTest(int value) : x(value) {}


    CTest(const CTest& v) = delete;
    CTest& operator=(const CTest& v) {
        return operator=<CTest>(v);
    }

    // non-const operator=
    template<class SrcType>CTest& operator=(SrcType& src)
    {
        x = src.x;
        std::cout << "non-const\n";
        return *this;
    }

    template<class SrcType>CTest& operator=(const SrcType& src)
    {
        x = src.x;
        std::cout << "const\n";
        return *this;
    }
};

int main()
{
    CTest   nonConstSrc{ 3 };
    const CTest constSrc{ 5 };
    CTest result;
    result = nonConstSrc;   // correctly calls non-const operator=
    result = constSrc;      // calls copy-assignment and forwards to template

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...