Страницы

Поиск по вопросам

воскресенье, 22 декабря 2019 г.

CLion не воспринимает запись вида `T const& a` при описании функции

#cpp #clion #const #address #code_analyst


Есть некоторый класс Vector2 с перегруженным оператором (возможно аналогичное
поведение и с обычными ф-циями, но из-за непредсказуемого поведения (об этом позже)
достоверно проверить не удалось):

template 
class Vector2
{
    ...
    template  // объявляем дружественную ф-цию шаблонной
    friend Vector2 operator +(Vector2 const& l_v, Vector2 const& r_v);
    ...
}


Обращу внимание на форму записи константной ссылки - T const& a, а не const T& val.
Потом это будет играть важную роль.

Но при попытке описать эту ф-цию, а именно:

template 
Vector2 operator +(Vector2 const& l_v, Vector2 const& r_v)
{ ... }


начинается самое интересное, а именно абсолютно непредсказуемое поведение анализа
кода "на лету", от подобных генераций кода при попытке автоматической реализации объявленной
ф-ции:

template  Vector2 operator+(Vector2 < T >

const & l_v,

Vector2 const& r_v

)
{
return
Vector2();
}
// да-да, именно в таком виде, со всеми отступами


до категоричного отказа анализировать код, подчёркивая каждое второе слово красным
цветом.

В целом, иногда (очень редко), CLion воспринимает всё правильно, но через время снова
начинает деградировать.

А теперь к T const& a. Дело в том, что если поменять именно в описании ф-ции запись
на const T& a, то всё будет работать более чем нормально. Вид записи в объявлении ф-ции
никак не влияет.



Безусловно, можно было бы просто перейти на запись вида const T& a, да и код-то компилируется,
и это лишь визуальная помощь от данной IDE, но я бы не стал писать этот вопрос, если
бы не хотел решить эту проблему, поэтому хотелось бы понять, как это возможно сделать.



P.S.
CLion версии 2018.1, но также проверялось на 2018.1.6, на другом устройстве, где
результат был таким же.
    


Ответы

Ответ 1



Так как в функции и левый операнд и правый имеют тип класса, то такую функцию другом обьявить бессмысленно. Во вторых, если работа ыункции никак не связана с состоянием обьекта, и результатом функции является совершенно другой обьект, то вообше лучше избавить интерфейс класса от этой функции и определить ее отдельно (но в одном модуле). Например: class Integer { int k; public: Integer(int n = 0) : k(n){} //модификаторы //т.е. мы изменяем состояние обьекта *this Integer& operator +=(const Integer& r_v) { k += r_v.k; return *this; } //... //селекторы int get_data() const { return k; } // функция выдает состояние обьекта }; // operator+ (и подобные методы) определяем вне класса inline Integer operator +(const Integer& l_v, const Integer& r_v) { Integer t(l_v); t += r_v; return t; } Такой подход избавляет класс от лишнего интерфейса и наиболее логичен. И если этот подход применить в написании вашего класса, то все становится гораздо проще... template class Vector2 { //... public: Vector2& operator +=(Vector2 const& r_v); }; template inline Vector2 operator +(Vector2 const& l_v, Vector2 const& r_v) {...}

Ответ 2



Дружественной вы объявили какую-то странную функцию template friend Vector2 operator +(Vector2 const& l_v, Vector2 const& r_v); У этой функции есть неименованный шаблонный параметр, который нигде не используется в сигнатуре функции. Т.е. вы объявили template friend Vector2 operator +(Vector2 const& l_v, Vector2 const& r_v); (я просто дал имя U вашему безымянному параметру). При этом, например, для класса Vector2 другом будет template friend Vector2 operator +(Vector2 const& l_v, Vector2 const& r_v); Зачем вы ввели этот ненужный параметр - не ясно. К функции, определенной вами ниже template Vector2 operator +(Vector2 const& l_v, Vector2 const& r_v) { ... } ваше объявление никакого отношения не имеет. Здесь у вас параметр шаблона прямо влияет на сигнатуру функции. Это определение другом вашего класса не является. Это мешанина, наверное, и сбивает с толку анализатор кода. А теперь к T const& a. Дело в том, что если поменять именно в описании ф-ции запись на const T& a, то всё будет работать более чем нормально. Нет. Ничего работать нормально не будет. Как я сказал выше, ваша функция другом класса не является, со всеми вытекающими последствиями. О том, как правильно объявлять шаблонных друзей, тут уже не раз писалось Перегрузка шаблонных операторов с разделением на описание и имплементацию Доступ к привату через friend Ссылка на неразрешенный внешний элемент Вы, по-видимому, пытались реализовать именно template class Vector2 { ... template // объявляем дружественную ф-цию шаблонной friend Vector2 operator +(Vector2 const& l_v, Vector2 const& r_v); ... }; но почему вы решили, что этот вариант можно "сократить" до вашего - не ясно. Также вы можете обойтись вообще без дополнительной шаблонности вашего оператора, но при этом его придется определять прямо в классе template class Vector2 { ... friend Vector2 operator +(Vector2 const& l_v, Vector2 const& r_v) { // Определение придется писать прямо сюда } ... }; ибо С++ не предоставляет синтаксиса для определения такого оператора за пределами класса.

Комментариев нет:

Отправить комментарий