Почему я получаю предупреждение о Wsign-конверсии?

У меня есть следующий код:

template <typename T>
struct wrapper {
    T t;
    operator T() { return t; }
    T get() { return t; }
};

int main() {
    int a[10];
    int* x = a;
    wrapper<long unsigned int> y{2};
    std::cout << (x + y); // warning
}

Когда я компилирую его на gcc (проверено на 7.3.0 и 8.2.0) с -Wsign-conversion, я получаю «предупреждение: преобразование в 'long int' из 'long unsigned int' может изменить знак результата». Если y имеет тип long unsigned int, предупреждения нет. Более того, когда я явно вызываю y.get(), также нет предупреждения:

std::cout << (x + y.get()); // this is ok

Почему это так? Существуют ли какие-то особые правила для арифметики указателей, которые нельзя использовать при использовании пользовательского преобразования?

Обратите внимание, что арифметика указателей IIRC определена только для элементов массива.

Daniel Langr 05.12.2018 10:38

Для x + y выполняется разрешение перегрузки и выбирается встроенный кандидат T* operator+(T*, std::ptrdiff_t);. Однако преобразование из long unsigned int в std::ptrdiff_t фактически не происходит, и кажется, что GCC игнорирует это при создании предупреждения.

cpplearner 05.12.2018 10:39

@DanielLangr правильно, я отредактировал вопрос, так что теперь a является типом массива.

Igor 05.12.2018 10:48
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
3
1 550
1

Ответы 1

Похоже на проблему / ошибку компилятора

(thanks @liliscent for correcting what I said here earlier)

Во-первых, давайте сделаем один MCVE для всего упомянутого вами утверждения:

#include <iostream>

template <typename T>
struct wrapper {
    T t;
    operator T() const { return t; }
    T get() const { return t; }
};

int main() {
    int a[10];
    int* x { a } ;
    wrapper<long int> y1{2};
    wrapper<unsigned int> y2{2};
    wrapper<long unsigned int> y3{2};

    std::cout << (x + y1) << '\n';
    std::cout << (x + y2) << '\n';
    std::cout << (x + y3) << '\n'; // this triggers a warning
    std::cout << (x + y3.get()) << '\n';
}

и используя GCC 8.2.0, мы получить:

<source>: In function 'int main()':
<source>:20:23: warning: conversion to 'long int' from 'long unsigned int' may change the sign of the result [-Wsign-conversion]
     std::cout << (x + y3) << '\n';
                       ^~
Compiler returned: 0

по ссылке вы увидите, как:

  • GCC выдает эту ошибку во всех (последних) версиях.
  • Clang выдает эту ошибку в версии 6.0.
  • Clang действительно, нет выдает эту ошибку с версией 7.0.

Так что это должен быть какой-то угловой случай w.r.t. соответствие стандартам.

... но не заходите на территорию "Here Be Dragons".

Теперь я уверен, что есть сложное техническое объяснение того, почему вы получаете ошибку только на 3-м из этих потоковых операторов. Но я утверждаю, что это не имеет значения с точки зрения практического использования. Если вы будете добавлять в указатели только правильные целые числа - как вы это делаете с оператором .get() - вы не получите этого предупреждения.

Видите ли, вы пытаетесь добавить к указателю определяемый пользователем тип, что обычно не имеет особого смысла. Верно, что ваша структура может быть преобразована в целое число, но, полагаясь на это преобразование, неявно открывает вам такие вещи, как выбор преобразования другого операнда или другие пути преобразования, которые вы не рассматривали. Более того, это случаи, когда вы можете «поразить» некоторую эзотерическую оговорку в стандарте, касающуюся того, когда неявные преобразования допустимы, что может или не может быть реализовано компилятором с идеальной корректностью (см. Комментарий @ cppcleaner).

Так что просто используйте x + y3.get() в своем коде, и вам не придется беспокоиться об этих тайных уголках.

Я привел аналогичный аргумент в этот ответ относительно использования индекса 0 пустой строки (да, это вещь).

Боюсь, это не отвечает на поставленный вопрос. Я бы полностью понял, что это вторая часть ответа, где первая часть объясняет причину. Чтобы ответ был таким: «вот почему; но, пожалуйста, сделайте что-нибудь разумное, например, это». Но без 1-й части это не ответ. Понимание того, почему что-то происходит, может быть более ценным, чем «решение, которое работает, потому что оно работает».

Angew is no longer proud of SO 05.12.2018 10:53

@Angew: В некоторых случаях важно понять, почему что-то происходит. В этом случае я утверждаю, что для нет важно понять, почему это происходит (если вы не изучаете поведение GCC, а не пишете C++). Но я постараюсь немного отредактировать, чтобы это подчеркнуть.

einpoklum 05.12.2018 10:56

@Angew ... но все равно добавил первую часть к ответу.

einpoklum 05.12.2018 11:02
Похоже, лязг вас не предупреждает -> Неправильно. Вы должны передать -Wsign-conversion, чтобы включить предупреждение, или -Weverything
llllllllll 05.12.2018 11:22

@einpoklum Предупреждение существует в clang 6.0, но не в 7.0. скорее всего ошибка.

llllllllll 05.12.2018 12:37

Другие вопросы по теме