Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.86/7: Рейтинг темы: голосов - 7, средняя оценка - 4.86
 Аватар для eva2326
1673 / 501 / 107
Регистрация: 17.05.2015
Сообщений: 1,518

баг msvc, или компиляторы право имеют? (отбрасывание const для возвращаемого типа приводит к ошибке ODR)

25.02.2020, 18:03. Показов 1885. Ответов 12
Метки bug, gcc, msvc (Все метки)

Студворк — интернет-сервис помощи студентам
Всем привет!

Рассмотрим пример:

C++
1
2
// header
void foo();
C++
1
2
3
// cpp
#include <iostream>
const void foo() { std::cout << "hello\n"; }
Тип возвращаемого значения отличается лишь наличием/отсутствием квалификатора const у определения.
Корректен ли данный код?

Должны ли компиляторы различать const t и t для не-классовых возвращаемых типов?

Должны ли компиляторы различать const void и void?

Вот всё, что я нашла по этому поводу в стандарте языка:
6.10 Lvalues and rvalues
Unless otherwise indicated (8.2.2), a prvalue shall always have complete type or the void type. A glvalue
shall not have type cv void
Следующий текст так же относится к этой же секции, но взят из другого документа:

Whenever a glvalue appears as an operand of an operator that expects a prvalue for that operand, the lvalue-to-rvalue, array-to-pointer, or function-to-pointer standard conversions are applied to convert the expression to a prvalue. [ Note: An attempt to bind an rvalue reference to an lvalue is not such a context; see [dcl.init.ref]. — end note ] [ Note: Because cv-qualifiers are removed from the type of an expression of non-class type when the expression is converted to a prvalue, an lvalue of type const int can, for example, be used where a prvalue of type int is required. — end note ]
Не уверена, что эти текста как то проливают свет на проблему.

В интернетах пишут, что компилятор действительно может отбросить квалификатор у возвращаемого значения не-классового-типа.
И что можно запросто столкнуться с ситуацией, когда линкеру действительно окажется все равно: const t или t.

Что думают об этом сами компиляторы/линкеры?

Если вместо void использовать double, тогда линкер msvc2019 выдал ошибку:
Code
1
2
3
1>ConsoleApplication1.obj : error LNK2019: unresolved external symbol "double __cdecl foo(void)" (?foo@@YANXZ) referenced in function main
1>  Hint on symbols that are defined and could potentially match:
1>    "double const __cdecl foo(void)" (?foo@@YA?BNXZ)
Если же использовать void, тогда компилируется/запускается без проблем.
То бишь, msvc2019 не различает конкретно void foo() и const void foo()

К сожалению нет под рукой gcc/clang, что бы сделать многофайловый проект.
Но на онлайн компиляторе они у меня отработали корректно.

Объявления типов агрят gcc и clang:
C++
1
2
3
4
int main()
{
    using f = const void();
}
gcc:
Code
1
2
3
4
source_file.cpp: In function ‘int main()’:
source_file.cpp:4:26: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
     using f = const void();
                          ^
clang:
Code
1
2
3
source_file.cpp:16:16: warning: 'const' type qualifier on return type has no effect [-Wignored-qualifiers]
    using f2 = const void();
               ^~~~~~
msvc:
Никаких предупреждений.

Забавно, gcc и clang предупреждают, но отрабатывают корректно.
msvc ни о чем не предупреждает, но отрабатывает неправильно.

В следующем коде:

https://rextester.com/ODDZ46574

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
 
template<class t> struct view;
    
template<class r, class... args> struct view<r(args...)>
{
    view()
    {
        std::cout << std::is_const<r>::value << '\n';
    }
};
 
int main()
{
    using f1 = void();
    using f2 = const void();
    
    std::cout << "is_same ? " << std::is_same<f1, f2>::value << '\n';
 
    view<f2>();    
    view<f1>();    
}
Компиляторы msvc2015, msvc2017, msvc2019 либо всегда показывают 1, либо всегда показывают 0
Зависит от того, какой шаблон инстанцирован первым.

Если первым инстанцировать шаблон для f2 (как в примере выше), тогда результат всегда будет 1.

Если же поменять местами:
C++
1
2
    view<f1>();    
    view<f2>();
Тогда всегда начнет показывать 0

Если при этом в проект добавить ещё один cpp (можно даже пустой),
тогда линкер msvc2019 сделает ошибку:
Code
1
ConsoleApplication1.obj : fatal error LNK1179: invalid or corrupt file: duplicate COMDAT '??0?$view@$$A6AXXZ@@QEAA@XZ'
Такая ошибка характерна для нарушения ODR.

Очень похоже на баг в компиляторах Visual Studio, связанный с тем,
что они не видят разницы между void foo() и const void foo()

Воспроизводится только с типом void
Для других типов, например для int(), const int() работают корректно.

Теперь два вопроса этой темы:
1.
Проблема из-за const void() - это баг компиляторов, или UB в юзерском коде?

2.
Можно ли как то починить сборку для msvc?
Мне нужно, что бы сборки всех компиляторов работали одинаково.

Добавлено через 25 минут
111
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
25.02.2020, 18:03
Ответы с готовыми решениями:

Почему отсутствие возвращаемого значения не приводит к ошибке компиляции?
Есть код //g++ 5.4.0 #include &lt;iostream&gt; struct foo { foo(){std::cout &lt;&lt; &quot;default\n&quot;;} foo(const foo&amp;...

AddNew приводит к ошибке Ошибка 800a0bb9 Аргументы имеют неверный тип, выходят за пределы допустимого диапазона ..
Вопрос возможно уже всем тут надоел, но я ничего подобного поиском не нашел... Нужен AddNew. Делаю так: set...

Использование int или void в качестве возвращаемого типа для main()
Когда использовать int main, а когда void main?

12
Mental handicap
 Аватар для Azazel-San
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
25.02.2020, 18:18
Цитата Сообщение от eva2326 Посмотреть сообщение
Должны ли компиляторы различать const t и t для не-классовых возвращаемых типов?
Должны ли компиляторы различать const void и void?
Вот всё, что я нашла по этому поводу в стандарте языка:
Есть еще такой пункт (чтобы это не значило):
Цитата Сообщение от http://eel.is/c++draft/expr#type-2
If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.
Добавлено через 9 минут
Цитата Сообщение от eva2326 Посмотреть сообщение
Должны ли компиляторы различать const t и t для не-классовых возвращаемых типов?
Должны ли компиляторы различать const void и void?
Но если отвечать конкретно на вопрос должны ли? То да должны, void и void const являются отдельными типами, так же как и все типы с определенными константами отличаются от их не константных аналогов.
1
 Аватар для eva2326
1673 / 501 / 107
Регистрация: 17.05.2015
Сообщений: 1,518
25.02.2020, 18:57  [ТС]
Цитата Сообщение от Azazel-San Посмотреть сообщение
If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.
rvalue/lvalue - это категории выражений.
Например:
C++
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
 
const int foo() { return 0; }
 
int main()
{
    using type = decltype(foo());
    const bool is = ::std::is_same<type, const int>::value;
    if(!is)
        std::cout << "discard const\n";
}
Результат:
Code
1
2
3
4
source_file.cpp:3:15: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
 const int foo() { return 0; }
               ^
discard const
Здесь decltype(foo()); - сложное выражение.
Оно состоит из выражения foo(), возвращающего prvalue const int, которое сначала должно деградировать до int, и только после этого продолжится вычисление итогового выражения decltype(int);.

В моём же случае баг заключается в том, что деградировало не выражение prvalue, которое возвращает функуция foo(), а вообще - сам тип.

https://rextester.com/ECTOGC76783

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
 
template<class t> struct view;
    
template<class... args> struct view<void(args...)>
{
    view()
    {
        std::cout << "void\n";
    }
};
 
template<class... args> struct view<const void(args...)>
{
    view()
    {
        std::cout << "const void\n";
    }
};
 
int main()
{
}
Code
1
2
source_file.cpp(21): error C2953: 'view<void(args...)>': class template has already been defined
source_file.cpp(8): note: see declaration of 'view<void(args...)>'
0
Mental handicap
 Аватар для Azazel-San
1246 / 624 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
25.02.2020, 19:37
Цитата Сообщение от eva2326 Посмотреть сообщение
В моём же случае баг заключается в том, что деградировало не выражение prvalue, которое возвращает функуция foo(), а вообще - сам тип.
А, вот оно что. Ну, из первого поста не все было так однозначно.

Добавлено через 2 минуты
Цитата Сообщение от Azazel-San Посмотреть сообщение
Но если отвечать конкретно на вопрос должны ли? То да должны, void и void const являются отдельными типами, так же как и все типы с определенными константами отличаются от их не константных аналогов.
Ну, вот так Студия различает:
C++
1
2
3
4
5
void const f() {}
 
int main() {
  void (*fptr)() = f;
}
Code
1
2
source_file.cpp(4): error C2440: 'initializing': cannot convert from 'const void (__cdecl *)(void)' to 'void (__cdecl *)(void)'
source_file.cpp(4): note: None of the functions with this name in scope match the target type
https://rextester.com/ZXUK74799
1
248 / 70 / 9
Регистрация: 22.07.2018
Сообщений: 321
26.02.2020, 01:40
Цитата Сообщение от eva2326 Посмотреть сообщение
Тип возвращаемого значения отличается лишь наличием/отсутствием квалификатора const у определения.
Корректен ли данный код?
Согласно [basic.link]/10, void foo(); и const void foo() {...} обозначают одну и ту же функцию (предполагается, что оба этих объявления в global scope), т.к. возвращаемый тип не участвует в определении того, обозначают ли два объявления одну и ту же функцию.
[basic.link]/11 требует, чтобы типы объявлений, обозначающих одну и ту же функцию, после всех type adjustments, были идентичными, что в данном коде не выполняется, т.к. отбрасывание top-level const у типа возвращаемого значения не входит в type adjustments (отбрасывание top-level const у параметров функции входит).
Таким образом, код нарушает [basic.link]/11.
Цитата Сообщение от eva2326 Посмотреть сообщение
Здесь decltype(foo()); - сложное выражение.
Оно состоит из выражения foo(), возвращающего prvalue const int, которое сначала должно деградировать до int, и только после этого продолжится вычисление итогового выражения decltype(int);.
decltype(foo()) это не выражение.
Миниатюры
баг msvc, или компиляторы право имеют? (отбрасывание const для возвращаемого типа приводит к ошибке ODR)  
1
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
26.02.2020, 13:03
Цитата Сообщение от eva2326 Посмотреть сообщение
Проблема из-за const void() - это баг компиляторов, или UB в юзерском коде?
баг компиляторов.

проблема с манглингом имен.

манглинг должен учитывать всю совокупность данных о функции:
квалифицированное имя,
список параметров,
и тип возвращаемого значения

посмотри какие заманглиновые имена получаются у компиляторов Visual Studio:
C++
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
 
int foo()
{
    std::cout << __FUNCDNAME__ << '\n';
    return 0;
}
 
int main()
{
    foo();
}
Code
1
2
3
4
// const int   // ?foo@@YA?BHXZ
// int         // ?foo@@YAHXZ
// const void  // ?foo@@YAXXZ
// void        // ?foo@@YAXXZ
в случае с int, компилятор справился,
а вот в случае с void - лажанул.

Цитата Сообщение от eva2326 Посмотреть сообщение
Можно ли как то починить сборку для msvc?
Мне нужно, что бы сборки всех компиляторов работали одинаково.
починить этот косяк могут только в майкрософт.
а ты можешь использовать обходной путь:


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
 
template<class t> struct view;
    
template<class r, class... args> struct view<r(args...)>
{
    #ifdef _MSC_VER // Visual Studio`s compiler
    static_assert(
        ! ::std::is_same<r, const void>::value,
        "'const void' as return type is not supported"
    );
    #endif
 
    view(){}
};
 
int main()
{
    using f2 = const void();
    using f1 = void();
 
    view<f1>();    
 
    // error C2338: 'const void' as return type is not supported
    // view<f2>();   
}
типы то у тебя распознаются правильно.
значит можно просто запретить редкий кейс для мелкомягких.

ну и отписать им баг. пускай чинят.
1
248 / 70 / 9
Регистрация: 22.07.2018
Сообщений: 321
26.02.2020, 17:57
Цитата Сообщение от hoggy Посмотреть сообщение
манглинг должен учитывать всю совокупность данных о функции:
...
тип возвращаемого значения
Зачем?
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
27.02.2020, 01:56
Цитата Сообщение от argcargv Посмотреть сообщение
Зачем?
1.
функции из разных ед. трансляции могут различаться только типом возращаемого значения:

C++
1
2
3
4
5
// foo1.cpp
int foo() { return 0; }
 
// foo2.cpp
void foo() { }
2.
тип возвращаемого значения входит в сигнатуру шаблоно-функций:

C++
1
2
3
4
5
6
// могут быть определены как в одной, так и в различных ед. трансляции
template<class T> int foo(T)
    { return 0; }
 
template<class T> bool foo(T)
    { return false; }
0
248 / 70 / 9
Регистрация: 22.07.2018
Сообщений: 321
27.02.2020, 02:42
Цитата Сообщение от hoggy Посмотреть сообщение
функции из разных ед. трансляции могут различаться только типом возращаемого значения
Так такая программа ill-formed, NDR.
Цитата Сообщение от hoggy Посмотреть сообщение
тип возвращаемого значения входит в сигнатуру шаблоно-функций
Для шаблонов понятно. Но для не шаблонных учитывать тип возвращаемого значения при манглинге не обязательно.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
28.02.2020, 17:38
Цитата Сообщение от argcargv Посмотреть сообщение
Так такая программа ill-formed, NDR.
обоснуй.

Добавлено через 2 минуты
Цитата Сообщение от argcargv Посмотреть сообщение
Но для не шаблонных учитывать тип возвращаемого значения при манглинге не обязательно.
на этапе линковки самого понятия "шаблон" уже не существует.
0
248 / 70 / 9
Регистрация: 22.07.2018
Сообщений: 321
28.02.2020, 18:27
Цитата Сообщение от hoggy Посмотреть сообщение
обоснуй.
В пятом посте уже было обосновано.
Цитата Сообщение от hoggy Посмотреть сообщение
на этапе линковки самого понятия "шаблон" уже не существует.
Спасибо, Капитан К. О.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
28.02.2020, 19:17
Цитата Сообщение от argcargv Посмотреть сообщение
В пятом посте уже было обосновано.
там нет ничего, кроме твоего голословного бла бла бла.

и ладно бы оно ещё хотя бы практикой подтверждалось,
так нет же.

вот это:
Цитата Сообщение от argcargv Посмотреть сообщение
Согласно [basic.link]/10
не является ссылкой на источник.
поскольку отсутствует сама ссылка.
(твой любимый Кэп)


язык не запрещает создавать какие угодно имена функции в различных областях видимости.
и до тех пор, пока они не конфликтуют - проблем никаких.

C++
1
2
3
4
5
// foo1.cpp
#include <iostream>
int foo() { std::cout << "int foo()\n"; return 0; }
 
void work1() { foo(); }
C++
1
2
3
4
5
// foo2.cpp
#include <iostream>
void foo() { std::cout << "void foo()\n"; }
 
void work2() { foo();  }

C++
1
2
3
4
5
// foo.h
 
#pragma once
void work1();
void work2();

C++
1
2
3
4
5
6
7
// main.cpp
#include "foo.h"
int main()
{
    work1();
    work2();
}
Цитата Сообщение от argcargv Посмотреть сообщение
Спасибо, Капитан К. О.
не знаю, зачем ты задаёшь капитанские вопросы.
0
248 / 70 / 9
Регистрация: 22.07.2018
Сообщений: 321
28.02.2020, 19:41
Цитата Сообщение от hoggy Посмотреть сообщение
там нет ничего, кроме твоего голословного бла бла бла.
Включи картинки в браузере, там ещё скрин приложен.
Цитата Сообщение от hoggy Посмотреть сообщение
и ладно бы оно ещё хотя бы практикой подтверждалось,
так нет же.
Видимо, практика настолько же ограниченая, как умственные способности.
Цитата Сообщение от hoggy Посмотреть сообщение
язык не запрещает создавать какие угодно имена функции в различных областях видимости.
Голословное бла бла бла
Цитата Сообщение от hoggy Посмотреть сообщение
C++
1
work
Doesn't work.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
28.02.2020, 19:41
Помогаю со студенческими работами здесь

Ключевое слово const для возвращаемого значения из функции
Думал что это означает следующее &quot;Нельзя изменить значение которое вернет функция&quot; Но проверил и оказалось что это не так ...

Ошибка "значение типа "const char [81]" нельзя использовать для инициализации сущности типа "const unsigned char [61]"
Прошу помощи, так как раньше прога работала, сейчас решил вернуться и выдает ошибку: #pragma once #include...

Запрос для вывода данных "продажа товара по типу" приводит к ошибке
Кто-нибудь,помогите,пожалуйста,с написанием запросов в Вижуал!!! В запросе необходимо вывести данные про &quot;продажа товара по...

Present у LPDIRECT3DSWAPCHAIN9 приводит к ошибке
Кратко: Есть главное окно приложения и есть второе окно приложения. Рисую в главное и во второстепенное окно. В главное как обычно, во...

timeSetEvent приводит к 0xC0000005 ошибке
Доброго времени суток! #include &lt;stdio.h&gt; #include &lt;Windows.h&gt; void timeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
13
Ответ Создать тему
Новые блоги и статьи
Переходник USB-CAN-GPIO
Eddy_Em 20.03.2026
Достаточно давно на работе возникла необходимость в переходнике CAN-USB с гальваноразвязкой, оный и был разработан. Однако, все меня терзала совесть, что аж 48-ногий МК используется так тупо: просто. . .
Оттенки серого
Argus19 18.03.2026
Оттенки серого Нашёл в интернете 3 прекрасных модуля: Модуль класса открытия диалога открытия/ сохранения файла на Win32 API; Модуль класса быстрого перекодирования цветного изображения в оттенки. . .
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие. Ссылка в Linux — это запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая ссылка» (hard link),. . .
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru