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

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

25.02.2020, 18:03. Показов 1742. Ответов 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
Ответ Создать тему
Новые блоги и статьи
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
Programma_Boinc 28.12.2025
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост. Налог на собак: https:/ / **********/ gallery/ V06K53e Финансовый отчет в Excel: https:/ / **********/ gallery/ bKBkQFf Пост отсюда. . .
Кто-нибудь знает, где можно бесплатно получить настольный компьютер или ноутбук? США.
Programma_Boinc 26.12.2025
Нашел на реддите интересную статью под названием Anyone know where to get a free Desktop or Laptop? Ниже её машинный перевод. После долгих разбирательств я наконец-то вернула себе. . .
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Рецензия / Мнение/ Перевод Нашел на реддите интересную статью под названием The Thinkpad X220 Tablet is the best budget school laptop period . Ниже её машинный перевод. Thinkpad X220 Tablet —. . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru