Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 08.02.19 10:40
Оценка:
Есть простейший код:
#include <iostream>

using namespace std;

template<int size>
class A 
{
};

template<int size>
A<size> operator&(const A<size>&, const A<size>&);

enum class En : int { a, b, c };

int main() {
    
    En en1 = En::a;
    En en2 = En::b;
    En en3 = En::a & En::b;
    
    return 0;
}

VS2013 ругается:
<source>(19): error C2784: 'A<size> operator &(const A<size> &,const A<size> &)': could not deduce template argument for 'const A<size> &' from 'En'
<source>(11): note: see declaration of 'operator &'
<source>(19): error C2676: binary '&': 'En' does not define this operator or a conversion to a type acceptable to the predefined operator


Хочется спросить у знатоков С++ — что происходит с перегрузкой, почему так и по стандарту ли это? Можно как-то поправить (сделать что бы operator& разрешался только для класса A<uint_t>) ?
Re: Перегрузка operator&
От: Muxa  
Дата: 08.02.19 10:53
Оценка:
V>Можно как-то поправить (сделать что бы operator& разрешался только для класса A<uint_t>) ?
Перенести его декларацию внутрь класса.
Re[2]: Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 08.02.19 10:57
Оценка:
Здравствуйте, Muxa, Вы писали:

M>Перенести его декларацию внутрь класса.


На самом деле такой оператор у "боевого" класса есть. С ним нет проблем. Интересует именно как решить перегрузку свободного operator&.
Re: Перегрузка operator&
От: Maniacal Россия  
Дата: 08.02.19 11:08
Оценка: +3
Здравствуйте, Videoman, Вы писали:

V>Хочется спросить у знатоков С++ — что происходит с перегрузкой, почему так и по стандарту ли это? Можно как-то поправить (сделать что бы operator& разрешался только для класса A<uint_t>) ?


В примере вообще operator& описан для класса A<size>, а применяется на enum'ах, причём класса En. Ругается, что не может enum преобразовать в A
Re[3]: Перегрузка operator&
От: Muxa  
Дата: 08.02.19 11:48
Оценка:
M>>Перенести его декларацию внутрь класса.
V>На самом деле такой оператор у "боевого" класса есть. С ним нет проблем. Интересует именно как решить перегрузку свободного operator&.

Тогда зачем тебе нужно чтобы внешний оператор& выполнялся для класса A, если у этого класса есть внутренний оператор& ?
Re[2]: Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 08.02.19 12:20
Оценка:
Здравствуйте, Maniacal, Вы писали:

M>В примере вообще operator& описан для класса A<size>, а применяется на enum'ах, причём класса En. Ругается, что не может enum преобразовать в A


Так в этом и вопрос, почему?
Re[4]: Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 08.02.19 12:21
Оценка:
Здравствуйте, Muxa, Вы писали:

M>Тогда зачем тебе нужно чтобы внешний оператор& выполнялся для класса A, если у этого класса есть внутренний оператор& ?


Что бы писать:
using A0 = A<0>;

A0 a =...
A0 b =...
A0 c = a + b
Re: Перегрузка operator&
От: σ  
Дата: 08.02.19 12:27
Оценка:
А в чём проблема? VC просто сообщает, что не смог использовать ни встроенный оператор &, ни шаблонный.
Re[5]: Перегрузка operator&
От: Muxa  
Дата: 08.02.19 12:33
Оценка:
Не вижу причин городить для этого отдельный внешний оператор:
https://gcc.godbolt.org/z/2icfNK

template<int size>
class A 
{
public:
    A<size> operator&(const A<size>&);
};

int main() {
    using A0 = A<0>;
    A0 a;
    A0 b;
    A0 c = a & b;

    return 0;
}
Отредактировано 08.02.2019 12:33 Muxa . Предыдущая версия .
Re[2]: Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 08.02.19 12:58
Оценка:
Здравствуйте, σ, Вы писали:

σ>А в чём проблема? VC просто сообщает, что не смог использовать ни встроенный оператор &, ни шаблонный.


Т.е. это такое завуалированное сообщение о том что не найден оператор для En ? Если у меня будет еще куча перегрузок, то он их все выведет?
Отредактировано 08.02.2019 12:59 Videoman . Предыдущая версия .
Re[3]: Перегрузка operator&
От: σ  
Дата: 08.02.19 13:02
Оценка:
V>Т.е. это такое завуалированное сообщение о том что не найден оператор для En ? Если у меня будет еще куча перегрузок, то он их все выведет?
Ну clang говорит
prog.cc:19:20: error: invalid operands to binary expression ('En' and 'En')
    En en3 = En::a & En::b;
             ~~~~~ ^ ~~~~~
/opt/wandbox/clang-head/include/c++/v1/bitset:1061:1: note: candidate template ignored: could not match 'bitset<_Size>' against 'En'
operator&(const bitset<_Size>& __x, const bitset<_Size>& __y) _NOEXCEPT
^
prog.cc:11:9: note: candidate template ignored: could not match 'A<size>' against 'En'
A<size> operator&(const A<size>&, const A<size>&);
        ^
1 error generated.


Разница с VC как я понимаю только в том, что VC генерирует по ошибке на кандидата, а clang перечисляет их в note к одной ошибке.
Re[3]: Перегрузка operator&
От: Maniacal Россия  
Дата: 08.02.19 13:03
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Здравствуйте, Maniacal, Вы писали:


M>>В примере вообще operator& описан для класса A<size>, а применяется на enum'ах, причём класса En. Ругается, что не может enum преобразовать в A


V>Так в этом и вопрос, почему?


У это нужно спросить у автора этих строк, зачем он так написал.

Добавь
En operator&(En e1, En e2);

И всё будет компилироваться
Re: Перегрузка operator&
От: Vamp Россия  
Дата: 08.02.19 13:07
Оценка:
Не понятно, что тебя удивляет. Оператор & для класс-перечислений не определен, шаблонный оператор тоже не подходит — вот компилятор и жалуется. А ты чего бы хотел?
Да здравствует мыло душистое и веревка пушистая.
Re[2]: Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 08.02.19 13:28
Оценка:
Здравствуйте, Vamp, Вы писали:

V>Не понятно, что тебя удивляет. Оператор & для класс-перечислений не определен, шаблонный оператор тоже не подходит — вот компилятор и жалуется. А ты чего бы хотел?


Ох. Трудно привести полностью не рабочий пример.
На самом деле у меня для Enum-а тоже есть опретор & но он хитро определен, примерно так:
namespace impl
{
    En operator&(const En&, const En&);
}

using impl;

В тестовых примерах у меня сейчас все компилируется, а вот в рабочем коде у меня постоянное срабатывает перегрузка оператора & для A<size> при использовании Enum-а, хотя должно разрешаться с помощью ADL.
во — воспроизвел пример
Re[3]: Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 08.02.19 13:30
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Здравствуйте, Vamp, Вы писали:


V>>Не понятно, что тебя удивляет. Оператор & для класс-перечислений не определен, шаблонный оператор тоже не подходит — вот компилятор и жалуется. А ты чего бы хотел?


V>Ох. Трудно привести полностью не рабочий пример.

V>На самом деле у меня для Enum-а тоже есть опретор & но он хитро определен, примерно так:
V>
V>namespace impl
V>{
V>    En operator&(const En&, const En&);
V>}

V>using impl;
V>

V>В тестовых примерах у меня сейчас все компилируется, а вот в рабочем коде у меня постоянное срабатывает перегрузка оператора & для A<size> при использовании Enum-а, хотя должно разрешаться с помощью ADL.
V>во — воспроизвел пример
А вот если убрать перегрузку для A<size>, то все начинает работать как нужно.
Re[4]: Перегрузка operator&
От: Muxa  
Дата: 08.02.19 13:43
Оценка:
V>А вот если убрать перегрузку для A<size>, то все начинает работать как нужно.

Вот еще пара рабочих примеров:
https://gcc.godbolt.org/z/6UGypb
https://gcc.godbolt.org/z/np-RS4

Выбирай который больше нравится и подходит
Похоже компилятор не может найти оператор объявленный в соседнем нэймспайсе, даже если ты делаешь юзинг.
Отредактировано 08.02.2019 13:45 Muxa . Предыдущая версия .
Re[5]: Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 08.02.19 14:05
Оценка:
Здравствуйте, Muxa, Вы писали:

M>Вот еще пара рабочих примеров:

M>https://gcc.godbolt.org/z/6UGypb
M>https://gcc.godbolt.org/z/np-RS4

M>Выбирай который больше нравится и подходит

M>Похоже компилятор не может найти оператор объявленный в соседнем нэймспайсе, даже если ты делаешь юзинг.

Спасибо конечно, но у меня проблема посложнее чем просто заставит код компилироваться. Хотелось бы понять что делать с этим и почему ADL сбоит
Re: Перегрузка operator&
От: koenjihyakkei Россия  
Дата: 08.02.19 14:21
Оценка:
Здравствуйте, Videoman, Вы писали:

https://stackoverflow.com/questions/27544893/why-doesnt-a-using-directive-affect-adl

10.3.4.3
A using-directive does not add any members to the declarative region in which it appears.

Отредактировано 08.02.2019 14:22 koenjihyakkei . Предыдущая версия .
Re[3]: Перегрузка operator&
От: Vamp Россия  
Дата: 08.02.19 14:30
Оценка:
V>В тестовых примерах у меня сейчас все компилируется, а вот в рабочем коде у меня постоянное срабатывает перегрузка оператора & для A<size> при использовании Enum-а, хотя должно разрешаться с помощью ADL.
V>во — воспроизвел пример

Во-первых, ADL тут вообще не причем. Чтобы было ADL, твоя функция должна жить в том же пространстве имен, что и тип. А у тебя ничего такого и в помине нет — тип живет в глобальном пространстве, функция в impl.

Во-вторых, ты, как мне кажется, не вполне понимаешь механизм работы using-директивы. В твоем случае, она работает как если бы твой оператор & для перечислений находился в глобальном пространстве имен.

В-третьих, в результате всего этого имеем горький катаклизм — в глобальном пространстве имен имеется подходящий оператор &, но в локальном пространстве имен (my) тоже имеется оператор &, и как только компилятор его находит, поиск прекращается. Вариантов исправления на самом деле много, зависит от того, что в конечном итоге тебе надо.

Простейший вариант — вместо using namespace impl делать using impl::operator&. Но в рабочем коде я не уверен, что это то, что тебе надо.
Да здравствует мыло душистое и веревка пушистая.
Отредактировано 08.02.2019 14:31 Vamp . Предыдущая версия .
Re[4]: Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 08.02.19 15:17
Оценка:
Здравствуйте, Vamp, Вы писали:

V>Во-первых, ADL тут вообще не причем. Чтобы было ADL, твоя функция должна жить в том же пространстве имен, что и тип. А у тебя ничего такого и в помине нет — тип живет в глобальном пространстве, функция в impl.


Хорошо, хорошо. Вот более полный пример. Идея была в том, что бы для строгих enum-ов автоматически определять битовые операции если надо с помощью декларации void enable_bitmask_operators(Enum);. А вот если раскомментарить A<size> operator&(const A<size>&, const A<size>&); то все перестает работать.

V>Во-вторых, ты, как мне кажется, не вполне понимаешь механизм работы using-директивы. В твоем случае, она работает как если бы твой оператор & для перечислений находился в глобальном пространстве имен.

Возможно что и так. Я всегда думал что using внутри namespace затаскивает указанный namespace туда

V>В-третьих, в результате всего этого имеем горький катаклизм — в глобальном пространстве имен имеется подходящий оператор &, но в локальном пространстве имен (my) тоже имеется оператор &, и как только компилятор его находит, поиск прекращается. Вариантов исправления на самом деле много, зависит от того, что в конечном итоге тебе надо.


У меня в реале функция шаблонная и я пока не пойму как это сделать правильно.
Re: Перегрузка operator&
От: rg45 СССР  
Дата: 08.02.19 23:08
Оценка:
Здравствуйте, Videoman, Вы писали:


V>Хочется спросить у знатоков С++ — что происходит с перегрузкой, почему так и по стандарту ли это? Можно как-то поправить (сделать что бы operator& разрешался только для класса A<uint_t>) ?


Так он и разрешается только для воплощений шаблонного класса A. А для перечислений En не разрешается, поэтому и возникает ошибка компиляции. Я полагаю, это очевидно и без ссылок на конкретные пункты стандарта, не так ли? А вот как должно выглядеть диагностическое сообщение об ошибке — этого в стандарте, конечно же нет, оно может иметь различную форму у разных компиляторов. Понятно, что разработчики компиляторов стараются сделать сделать свои диагностические сообщения максимально полезными и понятными. Ну а получается уже как получается.
--
Отредактировано 08.02.2019 23:12 rg45 . Предыдущая версия .
Re[3]: Перегрузка operator&
От: Vain Россия google.ru
Дата: 09.02.19 00:02
Оценка:
Здравствуйте, Videoman, Вы писали:

σ>>А в чём проблема? VC просто сообщает, что не смог использовать ни встроенный оператор &, ни шаблонный.

V>Т.е. это такое завуалированное сообщение о том что не найден оператор для En ? Если у меня будет еще куча перегрузок, то он их все выведет?
Это такие кривые сообщения об ошибках у компилятора студии
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[2]: Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 09.02.19 09:50
Оценка:
Здравствуйте, rg45, Вы писали:

R>Так он и разрешается только для воплощений шаблонного класса A. А для перечислений En не разрешается, поэтому и возникает ошибка компиляции. Я полагаю, это очевидно и без ссылок на конкретные пункты стандарта, не так ли? А вот как должно выглядеть диагностическое сообщение об ошибке — этого в стандарте, конечно же нет, оно может иметь различную форму у разных компиляторов. Понятно, что разработчики компиляторов стараются сделать сделать свои диагностические сообщения максимально полезными и понятными. Ну а получается уже как получается.


Не совсем. Вот тут я попытался обозначить проблему. Оператор& класса A<size> маскирует оператор& Enum-а. Если его убрать, то все ок.
#include <iostream>

using namespace std;

namespace impl
{
    template<typename Enum, class = decltype(enable_bitmask_operators(*(Enum*)nullptr))>
    inline Enum operator&(Enum left, Enum right)
    {
        using Underlying = std::underlying_type_t<Enum>;
        return static_cast<Enum>(static_cast<Underlying>(left) & static_cast<Underlying>(right));
    }
}

namespace my
{
using namespace impl;

enum class Enum : int { a, b, c };
void enable_bitmask_operators(Enum);

template<int size>
class A 
{
};

//template<int size>                      // Если это раскомментраить то то перестает компилироваться
//A<size> operator&(const A<size>&, const A<size>&);    // Если это раскомментраить то то перестает компилироваться

void func()
{
    Enum en1 = Enum::a;
    Enum en2 = Enum::b;
    Enum en3 = Enum::a & Enum::b;
}

} // namespace my

namespace my2
{
    using namespace impl;

    my::Enum en3 = my::Enum::a & my::Enum::b;
} // namespace my2

int main() 
{    
    return 0;
}
Re[3]: Перегрузка operator&
От: rg45 СССР  
Дата: 09.02.19 09:57
Оценка: 6 (1)
Здравствуйте, Videoman, Вы писали:

R>>Так он и разрешается только для воплощений шаблонного класса A. А для перечислений En не разрешается, поэтому и возникает ошибка компиляции. Я полагаю, это очевидно и без ссылок на конкретные пункты стандарта, не так ли? А вот как должно выглядеть диагностическое сообщение об ошибке — этого в стандарте, конечно же нет, оно может иметь различную форму у разных компиляторов. Понятно, что разработчики компиляторов стараются сделать сделать свои диагностические сообщения максимально полезными и понятными. Ну а получается уже как получается.


V>Не совсем. Вот тут я попытался обозначить проблему. Оператор& класса A<size> маскирует оператор& Enum-а. Если его убрать, то все ок.

V>
  ccode
V>
V>#include <iostream>

V>using namespace std;

V>namespace impl
V>{
V>    template<typename Enum, class = decltype(enable_bitmask_operators(*(Enum*)nullptr))>
V>    inline Enum operator&(Enum left, Enum right)
V>    {
V>        using Underlying = std::underlying_type_t<Enum>;
V>        return static_cast<Enum>(static_cast<Underlying>(left) & static_cast<Underlying>(right));
V>    }
V>}

V>namespace my
V>{
V>using namespace impl;

V>enum class Enum : int { a, b, c };
V>void enable_bitmask_operators(Enum);

V>template<int size>
V>class A 
V>{
V>};

V>//template<int size>                      // Если это раскомментраить то то перестает компилироваться
V>//A<size> operator&(const A<size>&, const A<size>&);    // Если это раскомментраить то то перестает компилироваться

V>void func()
V>{
V>    Enum en1 = Enum::a;
V>    Enum en2 = Enum::b;
V>    Enum en3 = Enum::a & Enum::b;
V>}

V>} // namespace my

V>namespace my2
V>{
V>    using namespace impl;

V>    my::Enum en3 = my::Enum::a & my::Enum::b;
V>} // namespace my2

V>int main() 
V>{    
V>    return 0;
V>}
V>



Ну так ответ здесь: http://rsdn.org/forum/cpp/7369795.1
Автор: koenjihyakkei
Дата: 08.02.19
. Используй using-declaration вместо using-directive:

   using impl::operator&;
--
Re[3]: Перегрузка operator&
От: σ  
Дата: 09.02.19 10:09
Оценка:
Я думаю про это лучше спросить на SO.
Re[4]: Перегрузка operator&
От: Videoman Россия https://hts.tv/
Дата: 09.02.19 12:54
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну так ответ здесь: http://rsdn.org/forum/cpp/7369795.1
Автор: koenjihyakkei
Дата: 08.02.19
. Используй using-declaration вместо using-directive:

R>
R>   using impl::operator&;
R>

Действительно работает, спасибо большое . Тогда, для общего развития, можно пояснить два вопроса:
1. Чем эта декларация отличается от using namespace impl ?
2. using impl::operator&; — "втягивает" объявление осех операторов& ?
Re[5]: Перегрузка operator&
От: rg45 СССР  
Дата: 09.02.19 13:09
Оценка: 3 (1) +1
Здравствуйте, Videoman, Вы писали:


V>Действительно работает, спасибо большое . Тогда, для общего развития, можно пояснить два вопроса:

V>1. Чем эта декларация отличается от using namespace impl ?

Это в стандарте есть, назвается argument dependent name lookup (ADL), кажется. Там перечислятеся в каких пространствах имен ищутся кандитаы на подстановку. В данном случае ADL сначала заглядывает в пространство имен, в котором определено перечисление En. Там искомый оператор не находится. Тогда поиск начинается от того пространства имен, в котором содержится обращение к оператору и выше, к обрамляющим пространствам имен, а также в пространсвах имен, которые включены при помощи using-directives. Это восхожение прекращается, как только искомое имя (operator&) находится. Тогда начинается анализ всех доступных в этом пространстве кандидатов на подстановку. В данном случае ADL натыкается на operator& для воплощений шаблонного класса A и в пространство impl уже не идет. В случае же использования using-declaration&, operator& из пространства имен impl оказывается внесенным в пространство имен my и находится как самый подходяший кандидат на подстановку.

V>2. using impl::operator&; — "втягивает" объявление осех операторов& ?


Да.

P.S. Простейшая иллюстрация сказанного:

https://ideone.com/o7bb32

namespace ns1
{

void foo(int){}

namespace ns2
{

void foo(){}

void test()
{
    //using ns1::foo;
    foo(42); // error: too many arguments to function ‘void ns1::ns2::foo()’
}

} // namespace ns2
} // namespace ns1

Расскомментирование using-declaration устраняет ошибку.
--
Отредактировано 09.02.2019 14:49 rg45 . Предыдущая версия . Еще …
Отредактировано 09.02.2019 13:20 rg45 . Предыдущая версия .
Отредактировано 09.02.2019 13:11 rg45 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.