is_error_code_enum - error: specialization after instantiation
От: Patalog Россия  
Дата: 25.06.21 20:26
Оценка:
Всем привет!

#include <system_error>

#define FAIL

enum class status
{
    ok = 0, fail = 1
};

inline
status get(status s1, status s2)
{
    return
#if defined(FAIL)
        std::min(s1, s2);
#else
    s1;
#endif
}

namespace std
{
    template <>
    struct is_error_code_enum<status>
        : true_type
    {};
}

int main()
{
    return 0;
}


gcc 9.3 -O3 -std=gnu++14

source>:24:12: error: specialization of 'std::is_error_code_enum<status>' after instantiation
24 | struct is_error_code_enum<status>
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:24:12: error: redefinition of 'struct std::is_error_code_enum<status>'
In file included from <source>:1:
/opt/compiler-explorer/gcc-9.3.0/include/c++/9.3.0/system_error:53:12: note: previous definition of 'struct std::is_error_code_enum<status>'
53 | struct is_error_code_enum : public false_type { };
| ^~~~~~~~~~~~~~~~~~
Compiler returned: 1


godbolt
Не воспроизводится при -O0 или -std=gnu++11 или если закомментрировать FAIL
В чем я не прав?


29.06.21 16:00: Перенесено из 'C/C++'
29.06.21 16:00: Перенесено из 'C/C++'
Почетный кавалер ордена Совка.
Re: is_error_code_enum - error: specialization after instantiation
От: reversecode google
Дата: 25.06.21 22:52
Оценка: 3 (1) -1
потому что неправильно используете
после использование енума как еррор кода его нельзя сравнивать
сравнивайте и используйте везде по коду std::error_code
а иначе какой смысл в том что вы делаете ?

ну или можете свой min в том месте определить, но это уг
Re[2]: is_error_code_enum - error: specialization after instantiation
От: Patalog Россия  
Дата: 26.06.21 08:37
Оценка:
Здравствуйте, reversecode, Вы писали:

R>потому что неправильно используете

R>после использование енума как еррор кода его нельзя сравнивать

? Хотелось бы подробностей. Я так понимаю проблема в том, что сравнение стало приводит к инстанцированию is_error_code_enum<status> до его специализации. Почему? В смысле почему приводит, а не почему до специализации.

In all cases, for the built-in operators, lhs and rhs must have either
arithmetic or enumeration type (see arithmetic comparison operators below)
...
If the operands have arithmetic or enumeration type (scoped or unscoped), usual arithmetic conversions are performed on both operands following the rules for arithmetic operators. The values are compared after conversions:

Comparison operators

an unscoped enumeration type whose underlying type is not fixed can be converted to the first type from the following list able to hold their entire value range: int, unsigned int, long, unsigned long, long long, or unsigned long long, extended integer types with higher conversion rank (in rank order, signed given preference over unsigned) (since C++11). If the value range is greater, no integral promotions apply;
an unscoped enumeration type whose underlying type is fixed can be converted to its underlying type, and, if the underlying type is also subject to integral promotion, to the promoted underlying type. Conversion to the unpromoted underlying type is better for the purposes of overload resolution;

Implicit conversions

Т.е. я так понимал, что сравнение enum'а идет через встроенное сравнение как арифметического типа. Судя по всему это не так. Хотелось бы понять почему. Выглядит так, что есть нек. глобальный шаблонный оператор сравнения, который enable_if<is_error_code_enum<T>::value>. При этом если заменить std::min именно на ручное сравнение, напр.
s1 < s2 ? s1 : s2;

не воспроизводится, как и при -O0 или -std=gnu++11? Что почейтать?

R>сравнивайте и используйте везде по коду std::error_code

R>а иначе какой смысл в том что вы делаете ?

Сравнение это легаси код, возможности менять его нет. Я просто привел здесь дистиллированный пример.
Почетный кавалер ордена Совка.
Re[3]: is_error_code_enum - error: specialization after instantiation
От: vopl Россия  
Дата: 26.06.21 09:34
Оценка: 10 (2)
Здравствуйте, Patalog, Вы писали:

P>Я так понимаю проблема в том, что сравнение стало приводит к инстанцированию is_error_code_enum<status> до его специализации. Почему? В смысле почему приводит, а не почему до специализации.

...
P>Т.е. я так понимал, что сравнение enum'а идет через встроенное сравнение как арифметического типа. Судя по всему это не так. Хотелось бы понять почему. Выглядит так, что есть нек. глобальный шаблонный оператор сравнения, который enable_if<is_error_code_enum<T>::value>. При этом если заменить std::min именно на ручное сравнение, напр.

ага, примерно так и есть. Этот дистилиянт сохраняет проблему, посмотри
namespace mystd
{

    template<typename T>
    struct myspec { static constexpr bool value = false; }; // std::is_error_code_enum


    struct Some// std::error_code
    {
        template<typename T, bool = myspec<T>::value>//инстанцирование myspec<T>
        Some(T) {}
    };

    void operator<(Some, Some);//инстанцирование конструктора Some

    template<typename T>
    constexpr const T& mymin(const T& a, const T& b) // std::min
    {
        return b < a ? b : a;// пробует void operator<(Some, Some);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////////
enum class status
{
    ok = 0, fail = 1
};

status get(status s1, status s2)
{
    return mystd::mymin(s1, s2);
}

namespace mystd
{
    template <>
    struct myspec<status> {static constexpr bool value = true; };
}

int main()
{
    return 0;
}
Re[4]: is_error_code_enum - error: specialization after instantiation
От: vopl Россия  
Дата: 26.06.21 10:24
Оценка:
Здравствуйте, vopl, Вы писали:

еще меньше
template<typename T> struct Myspec {};

struct Some
{
    template<typename T, int = sizeof(Myspec<T>)> Some(T) {}
};
void operator<(Some, Some);

enum class E{a=1, b=2};
int main()
{
    E::a < E::b;
    return 0;
}

template <> struct Myspec<E> {};
Re[5]: is_error_code_enum - error: specialization after instantiation
От: vopl Россия  
Дата: 26.06.21 12:03
Оценка: 72 (1)
Здравствуйте, vopl, Вы писали:

V>еще меньше


и глубже
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101222
Re[6]: is_error_code_enum - error: specialization after instantiation
От: Patalog Россия  
Дата: 26.06.21 14:18
Оценка:
Здравствуйте, vopl, Вы писали:

[]

V>и глубже

V>https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101222

Т.е. в сухом остатке это бага компилятора, а не ошибка в коде, праильно?
Есть варианты обкостылить, кроме как убрать специализацию
struct is_error_code_enum<status>

?
Почетный кавалер ордена Совка.
Re[7]: is_error_code_enum - error: specialization after instantiation
От: vopl Россия  
Дата: 26.06.21 14:46
Оценка: 3 (1)
Здравствуйте, Patalog, Вы писали:

P>Т.е. в сухом остатке это бага компилятора, а не ошибка в коде, праильно?

Не уверен на 100%, возможно, сейчас набегут gcc-шники и объяснят что это вовсе не бага а фича

P>Есть варианты обкостылить, кроме как убрать специализацию

P>
P>struct is_error_code_enum<status>
P>

P>?

сложно говорить по такому минимализированному кусочку, не понятно что можно менять а что нет. Навскидку,
можно убрать специализацию
можно отказаться от использования std::min, тогда operator< не будет искаться в std и не будет активироваться проблемный
можно переместить специализацию ближе к определению status, чтобы между ними не вклинивалось использование operator<
можно сделать get шаблонным, тогда он будет инстанцироваться к конце единицы трансляции, то есть, не между объявлением status и специализацией
наверняка еще варианты есть
Re[8]: is_error_code_enum - error: specialization after instantiation
От: Patalog Россия  
Дата: 26.06.21 19:12
Оценка:
Здравствуйте, vopl, Вы писали:

хъ

V>можно убрать специализацию


Это понятно.

V>можно отказаться от использования std::min, тогда operator< не будет искаться в std и не будет активироваться проблемный


Возможно это получится сделать в некоторых местах, но везде — однозначно нет.

V>можно переместить специализацию ближе к определению status, чтобы между ними не вклинивалось использование operator<


Не вариант, сам status в закрытом для такого рода изменений файле, то, куда и как он включается проконтролировать тоже не получится.

V>можно сделать get шаблонным, тогда он будет инстанцироваться к конце единицы трансляции, то есть, не между объявлением status и специализацией


Это примерно тоже самое что отказаться от std::min. Данный конкретный get я допустим поменяю, но все места где может что-то такое появится — нет.

V>наверняка еще варианты есть


Почетный кавалер ордена Совка.
Re[9]: is_error_code_enum - error: specialization after instantiation
От: vopl Россия  
Дата: 26.06.21 20:04
Оценка: 6 (1)
Здравствуйте, Patalog, Вы писали:

V>>наверняка еще варианты есть


P>


можно подcунуть свою перегрузку std::min и внутри нее как нибудь по хитрому организовать сравнение, чтобы std-шные операторы сравнения не увидели status, примерно так
  Скрытый текст
#include <system_error>

#define FAIL

enum class status
{
    ok = 0, fail = 1
};

namespace std
{
    constexpr status min(status a, status b)
    {
        return static_cast<int>(a) < static_cast<int>(b) ? 
            a : 
            b;
    }
}

inline
status get(status s1, status s2)
{
    return
#if defined(FAIL)
        std::min(s1, s2);
#else
    s1;
#endif
}

namespace std
{
    template <>
    struct is_error_code_enum<status>
        : true_type
    {};
}

int main()
{
    get(status{}, status{});
    return 0;
}
-хоть и UB но работать должно
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.