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

bug msvc2013 ? SFINAE std::void_t idiom not worked

13.04.2021, 16:26. Показов 1903. Ответов 3

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

Кто ни будь знает, почему в Visual Studio 2013 не работает идиома std::void_t ?

Следующий код корректен с точки зрения правил языка.
Но сборка msvc2013 слетает на static_assert:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <type_traits>
#include <iostream>
#include <string>
#include <vector>
 
namespace util
{
    template<class...> using void_t = void;
 
} // namespace util
 
typedef std::string      str_t;
typedef std::vector<int> vec_t;
 
typedef util::void_t<int, bool, char, float, double, short, long>
    chk;
  
template<class, class = void>
struct has_value_type
    : std::integral_constant<bool, false>
{};
 
template<class T>
struct has_value_type<T, util::void_t<typename T::value_type> > 
    : std::integral_constant<bool, true>
{};
 
static_assert(
    has_value_type<str_t>::value,
    "VEC_ERROR"
);
 
static_assert(
    has_value_type<vec_t>::value,
    "MAP_ERROR"
);
 
// error C2338: INT_ERROR
static_assert(
    !has_value_type<int>::value,
    "INT_ERROR"
);
 
int main()
{
    std::cout << "[ALL TESTS PASSED]\n";
}
Можно ли как ни будь подружить msvc2013 и std::void_t?
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
13.04.2021, 16:26
Ответы с готовыми решениями:

Streaming cluster replication bug or ne bug
Как только оно появилось с версии 8.5х - так сразу его рекомендовали отключить через DEBUG_SCR_DISABLED=1 )) Ну и было отключено. Думаю...

Erase-remove idiom: идиома tuple
#include &lt;tuple&gt; #include &lt;iostream&gt; #include &lt;vector&gt; #include &lt;ctime&gt; #include &lt;algorithm&gt; int main() { std::vector&lt;...

Как изменить главную страницу, где выводится It worked!?
Настроил Apache + Django и у меня выводится It worked!. Как изменить эту главную страницу? Например, вывести &quot;Hellow world&quot;.

3
Мозгоправ
 Аватар для L0M
1745 / 1039 / 468
Регистрация: 01.10.2018
Сообщений: 2,138
Записей в блоге: 2
15.04.2021, 01:33
Цитата Сообщение от eva2326 Посмотреть сообщение
Можно ли как ни будь подружить msvc2013 и std::void_t?
std::void_t это который since C++17?
Лучше взять другой компилятор.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
15.04.2021, 15:27
Лучший ответ Сообщение было отмечено eva2326 как решение

Решение

что бы решить эту проблему, нужно сначала понять суть этой проблемы.
а что бы понять в чем суть проблемы,
нужно сначала понять принцип действия техники void_t
а для того, что бы понять принцип действия техники void_t,
нужно сначала понять "принцип инстанцирования специализации шаблона по умолчанию"


итак, начнем по порядку.

принцип инстанцирования специализации шаблона по умолчанию:
---

гласит:
сначала компилятор подставляет значение по умолчанию,
а затем проверяет: если полученный набор параметров удовлетворяет специализации,
тогда он инстанцирует эту специализацию,
в противном случае инстанцируется главный шаблон.

таким образом, по умолчанию всегда запускается специализация, а не главный шаблон

пример:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
 
template<class t, bool = true>
struct example
{
    example() noexcept { std::cout << "general\n"; }
};
 
template<class t>
struct example<t, true>
{
    example() noexcept { std::cout << "example<t,true>\n"; }
};
 
int main()
{
    example<int> ex; // example<t,true>
}
если не указать второй параметр, то по умолчанию он будет true,
что соответствует специализации, и именно она и будет в итоге инстанцирована.

void_t
----
представляет собой алиас с типом void
C++
1
2
3
4
5
namespace std
{
    template<class...> using void_t = void;
 
} // namespace std
по правилам языка с++, псевдоним типа и сам тип - это одно и тоже.
другими словами, std::void_t<любые параметры> - на самом деле является просто void

теперь рассмотрим классический случай использования техники на практике:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <string>
 
template<class, class = void> struct has_value_type
    : std::false_type
{};
 
template<class T> 
struct has_value_type<T, std::void_t<typename T::value_type> > 
    : std::true_type
{};
 
static_assert(has_value_type<std::string>::value, "ERROR");
int main(){}
мы уже знаем, что если явно не указать значение по умолчанию,
то компилятор попытается инстанцировать дефолтную специализацию.
в данном случае компилятор будет искать специализацию <T,void>

а поскольку std::void_t<typename T::value_type> и есть void,
то именно эту специализацию и попытается инстанцировать компилятор по умолчанию:
C++
1
2
3
4
template<class T> 
struct has_value_type<T, std::void_t<typename T::value_type> > 
    : std::true_type
{};
но(!) с одним условием: тип данных std::void_t<typename T::value_type> должен быть валидным.
а валидным он может быть только и только в том случае,
если внутри T окажется доступным тип value_type

если же это условие не выполняется,
тогда вся специализация has_value_type<T, std::void_t<typename T::value_type> > становится не_валидной,
и выпадает из конкурса на инстанцирование.

в этом случае, компилятор, согласно правилам SFINAE,
должен выбрать оставшиёся единственным главный шаблон:
C++
1
2
3
template<class, class = void> struct has_value_type
    : std::false_type
{};
суть проблемы msvc2013
---
теперь, поняв как работает sfinae на основе void_t,
уже можно анализировать проблему компиляторов msvc2013.
следующий код иллюстрирует суть проблемы:
C++
1
2
3
4
5
6
7
8
9
10
11
12
template<class T, class VT = typename T::value_type> 
using enable_if_has_value_type_t = void;
 
template<class T, class = void>
struct has_value_type : std::false_type {};
 
template<class T>
struct has_value_type<T, enable_if_has_value_type_t<T> > 
  : ::std::true_type {};    
 
static_assert(!has_value_type<int>::value, "ERROR"); // upppssss
int main(){}
что здесь произошло?
компилятор успешно инстанцировал специализацию true_type,
невзирая на то, что запись class VT = typename T::value_type синтаксически не_корректна.

очевидно, что это произошло потому,
что компилятор и не пытался распарсить typename T::value_type

ещё раз внимательно смотрим на алиас:
C++
1
2
3
4
5
namespace std
{
    template<class...> using void_t = void;
 
} // namespace std
итоговый результат не_зависит от параметров шаблона.
старым компиляторам (msvc2013 или более старым) это даёт основание забить болт на "тяжелые" вычисления,
и минуя их, сразу выдать итоговый результат - void

кто-то скажет: но ведь параметры шаблона могут быть кривыми!
почему это компилятор их не проверяет?

на это в стандарте есть железобетонная отмазка:
делай нормально, и будет у тебя нормально.
а будешь делать криво - будет у тебя UB, и никто тебе ничего не должен.


компилятор имеет полное право закладываться на то,
что программист - не идиот, а значит не будет писать всякий бред.
нельзя сказать что компилятор msvc2013 содержит баг.
потому что для тогдашнего с++11 такое поведение было корректным.
в дальнейшем поведение было признано дефектом
и в настоящий момент компиляторы таки обязаны валидировать параметры в SFINAE контексте.

получилась забавная ситуация:
для изготовления самодельного std::void_t достаточно с++11
но для того, что бы оно корретно работало, требуется поддержка с++17

лекарство
---
самое простое и очевидное решение - использовать дискриминатор.
о дискриминаторах я уже писал несколько лет назад.
[Дизайн и эволюция] Дискриминация шаблона на примере макроса OUT_TO_STREAM

если вкратце:
дискриминатор - элемент конструкции,
который делает саму конструкцию более сложной,
но при этом итоговый результат остаётся без изменений
с помощью дискриминатора
можно заставить компилятор честно проверить все параметры шаблона:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <type_traits>
 
namespace util
{
    template<class... Args> struct discriminator
        { enum { value = 1 }; };
 
    template<class A, class... Args> struct discriminator<A, Args...>
        { enum { value = ::std::is_same<A,A>::value }; };
 
    template<class... Args> 
        using void_t = ::std::enable_if_t< discriminator<Args...>::value >;
 
} // namespace util
 
 
template<class, class = void>
struct has_value_type
    : std::integral_constant<bool, false>
{};
 
template<class T>
struct has_value_type<T, util::void_t<typename T::value_type> > 
    : std::integral_constant<bool, true>
{};
 
#include <iostream>
#include <vector>
#include <string>
#include <map>
 
using str_t = std::string;
using vec_t = std::vector<int>;
using map_t = std::map<int,int>;
 
static_assert(has_value_type<str_t>::value, "STR_ERROR");
static_assert(has_value_type<vec_t>::value, "VEC_ERROR");
static_assert(has_value_type<map_t>::value, "MAP_ERROR");
static_assert(!has_value_type<int>::value , "INT_ERROR");
 
int main()
{
    std::cout << "[ALL TESTS PASSED]\n";
}
в этом примере, что бы вычислить значение void_t,
компилятор вынужден инстанцировать дискриминатор,
который в свою очередь требует вычисления всех параметров шаблона,
что в свою очередь не позволяет компилятору их проигнорировать.

PROFIT???!!!!
3
 Аватар для eva2326
1673 / 501 / 107
Регистрация: 17.05.2015
Сообщений: 1,518
16.04.2021, 13:33  [ТС]
Цитата Сообщение от hoggy Посмотреть сообщение
лекарство
Афигеть!!!
Лекарство сработало не только для msvc2013, а вообще - для всей линейки студий.
Это сработало даже для msvc2008.

Единственное:

Цитата Сообщение от hoggy Посмотреть сообщение
discriminator
Здесь ошибка.
Правильно должно быть:

C++
1
2
3
4
5
6
7
8
9
            template<class... Args> struct discriminator_
                { enum { value = 1 }; };
 
            template<class A, class... Args> struct discriminator_<A, Args...>
            { 
                using x = ::std::detail::discriminator_<Args...>;
                enum { v1 = ::std::is_same<A, A>::value };
                enum { value = v1 && x::value };
            };
Для компиляторов, которые не поддерживают вариадик-шаблоны


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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
    // msvc2012 or older
 
    #define dVARIADIC_7 \
        class t1, class t2 = empty, class t3 = empty, class t4 = empty, \
        class t5 = empty, class t6 = empty, class t7  = empty
 
    namespace util
    {
        struct empty;
        namespace detail
        {
            template <bool, class t = void> 
                struct enable_if_;
 
            template <class t> struct enable_if_<true, t>
                { typedef t type; };
 
            template<class a, class b> struct is_same_
                { enum { value = 0 }; };
 
            template<class t> struct is_same_<t, t>
                { enum { value = 1 }; };
 
 
            template<dVARIADIC_7> 
            struct discriminator_
            {
                enum { r1 = is_same_<t1, t1>::value };
                enum { r2 = discriminator_<t2, t3, t4, t5, t6, t7>::value };
                enum { value = r1 && r2 };
            };
 
            template<class t1, class t2, class t3, class t4, class t5, class t6> 
            struct discriminator_<t1, t2, t3, t4, t5, t6, empty>
            {
                enum { r1 = is_same_<t1, t1>::value };
                enum { r2 = discriminator_<t2, t3, t4, t5, t6>::value };
                enum { value = r1 && r2 };
            };
 
            template<class t1, class t2, class t3, class t4, class t5> 
            struct discriminator_<t1, t2, t3, t4, t5, empty, empty>
            {
                enum { r1 = is_same_<t1, t1>::value };
                enum { r2 = discriminator_<t2, t3, t4, t5>::value };
                enum { value = r1 && r2 };
            };
 
            template<class t1, class t2, class t3, class t4> 
            struct discriminator_<t1, t2, t3, t4, empty, empty, empty>
            {
                enum { r1 = is_same_<t1, t1>::value };
                enum { r2 = discriminator_<t2, t3, t4>::value };
                enum { value = r1 && r2 };
            };
 
            template<class t1, class t2, class t3> 
            struct discriminator_<t1, t2, t3, empty, empty, empty, empty>
            {
                enum { r1 = is_same_<t1, t1>::value };
                enum { r2 = discriminator_<t2, t3>::value };
                enum { value = r1 && r2 };
            };
 
            template<class t1, class t2> 
            struct discriminator_<t1, t2, empty, empty, empty, empty, empty>
            {
                enum { r1 = is_same_<t1, t1>::value };
                enum { r2 = discriminator_<t2>::value };
                enum { value = r1 && r2 };
            };
 
            template<class t1> 
            struct discriminator_<t1, empty, empty, empty, empty, empty, empty>
            {
                enum { value = is_same_<t1, t1>::value };
            };
 
        } // namespace detail
 
        template<dVARIADIC_7> struct void_type
        {
            typedef typename detail::enable_if_<
                detail::discriminator_<t1,t2,t3,t4,t4,t6,t7>::value 
            >::type 
                type;
        };
 
    } // namespace util


К сожалению, радоваться пришлось не долго.
Но это уже другая история.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
16.04.2021, 13:33
Помогаю со студенческими работами здесь

В чем разница между MSVC2010 и MSVC2013?
Приветствую! В чем разница между MSVC2010 и MSVC2013? В чем и где это может проявиться? Что стало лучше в, наверно, следующей версии...

Qt msvc2013: C1083 : не находятся файлы включения
Использую OpenCV, и компилятор не видит КВ-ые файлы. При этом Qt Creator их видит и преспокойно открывает. Просто не понимаю, чего не видит...

Boost.Python - линковка со статическом питоном (MSVC2013)
Добрый день! Уже два дня пытаюсь собрать Boost.Python со статической библиотекой Python (чтобы не было зависимость от .dll). 1....

Не воспринимает ни std::cout, ни std::cin. Вобщем ничего из std. Также не понимает iostream
Здравствуйте! Я хотел начать изучать язык C++. Набрал литературы. Установил Microsoft Visual C++ 2005 Express Edition. Образ диска...

ошибка error: cannot convert 'std::string {aka std::basic_string<char>}' to 'std::string* {aka std::basic_stri
на вод поступают 2 строки типа string. определить количество вхождений строки 2 в строку 1 ошибка error: cannot convert 'std::string {aka...


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

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
Новые блоги и статьи
Учёным и волонтёрам проекта «Einstein@home» удалось обнаружить четыре гамма-лучевых пульсара в джете Млечного Пути
Programma_Boinc 01.01.2026
Учёным и волонтёрам проекта «Einstein@home» удалось обнаружить четыре гамма-лучевых пульсара в джете Млечного Пути Сочетание глобально распределённой вычислительной мощности и инновационных. . .
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
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/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru