LINUX.ORG.RU

Почему компилируется такой код?

 ,


0

2
#include<iostream>
#include<stack>

struct Node
{
 double r=0;
 explicit Node(double rr): r(rr) {}
 explicit Node(bool bb): r(-1.0) {}
};

int main()
{
 std::stack<Node> st;
 double r=10.0;

 st.emplace(&r);
 std::cout<<st.top().r<<std::endl;
}

Код компилируется, печатает -1, но я не понимаю почему. Каким образом st.emplace(double*) вызывает конструктор Node(bool), даже если он explicit?


Ты передал указатель. Компилятор по-твоему должен был его разыменовывать что-ли?

Указатель неявно умеет конвертироваться в bool.

int *a;
if (a) {} // Этот код тебя не удивляет?
ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Было бы просто Node(bool bb): r(-1.0) {}, вопроса б не было. Но стоит explicit, который неявные преобразования должен отсекать.

JaM
() автор топика

Потому что для pointer->boolean не действует explicit. Enjoy.

Скорее всего это исключение сделано только ради operator bool, как показали выше. Но каким-то хером оно распространяется и на конструкторы.

filosofia
()
Ответ на: комментарий от filosofia

Потому что для pointer->boolean не действует explicit. Enjoy.

Охренеть. То есть все классы, содержащие конструктор из bool, будут преспокойно инициализироваться bool* и прочими указателями. Ну, спасибо разработчикам за такое счастье. Это можно как-то побороть?

JaM
() автор топика
Последнее исправление: JaM (всего исправлений: 1)

Emplace просто пробросит аргументы до конструктора.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Ага, только при этом Node i{0}; благополучно выдаёт ошибку, так как никакого неявного преобразования не производится.

JaM
() автор топика
Ответ на: комментарий от ox55ff

Node i{nullptr}; тоже выдаёт ошибку. На 20-м, но 11-й тоже самое делает.

JaM
() автор топика

Каким образом st.emplace(double*) вызывает конструктор Node(bool), даже если он explicit?

explicit запрещает неявное преобразование bool в Node, но здесь его нет, st.emplace(&r) содержит вызов Node(&r), а double* в bool преобразуется на ура

annulen ★★★★★
()

Комитет фиксил похожее для C++17:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1957r2.html

Тут будет ошибка:

#include<iostream>
#include<stack>

struct Node
{
 double r=0;
 explicit Node(double rr): r(rr) {}
 explicit Node(bool bb): r(-1.0) {}
};

int main()
{
 double r=10.0;
 Node n{&r};
 std::cout<<n.r<<std::endl;
}

Может и твой пример пофиксят, напиши кому-нибудь оттуда или сам напиши бумагу и отправь им…

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от JaM

Ты можешь попробовать выкрутить warnings (-Wall -Wextra -pedantic). У меня сейчас нет компилятора, чтобы проверить. Как workaround можно завернуть bool в отдельную структуру.

Я не сильно слежу за плюсами после 17 стандарта, но вроде там были какое-то proposals в стандарт, чтобы победить это.

filosofia
()
Ответ на: комментарий от fsb4000

Да, я эту доку нашёл. В c++20 её включили. gcc, кстати, на этот код выдаёт только ворнинг, а шланг ошибку и ворнинг.

По идее, тогда получается баг в компиляторе, а не в стандарте? emplace же должен вызывать конструктор, который не должен компилироваться?

JaM
() автор топика
Последнее исправление: JaM (всего исправлений: 1)
Ответ на: комментарий от JaM

Я сейчас напишу в багтрекер Microsoft, посмотрим что ответят.

Судя по «Converting from T* to bool should be considered narrowing» желание запретить такое преобразование у комитета есть :)

fsb4000 ★★★★★
()

TL;DR:

указатель неявно к булевому значению скастился

EugeneBas ★★
()
Ответ на: комментарий от JaM

emplace же должен вызывать конструктор, который не должен компилироваться?

Он и вызывается, но с круглыми скобками, а не с фигурными. (через allocator.construct вначале)

https://gcc.godbolt.org/z/e8xEo6bG1

Я спросил можем мы поменять на фигурные или весь мир сломается после этого, посмотрим на ответ что именно сломается если заменить в исходниках emplace стандартных контейнеров на фигурные скобки.

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от filosofia

Ну и, если это публичный багтрекер, дай ссылку.

Проблемы связанные с Visual C++, Visual C, STL, CRT и всяким связанным с .NET и Asure можно репортить сюда: https://developercommunity.visualstudio.com/home

Но я пока решил спросить просто у разработчиков в публичном чатике, потому что решил что так будет быстрее:

Join our Discord server. https://discord.gg/XWanNww

fsb4000 ★★★★★
()
Ответ на: комментарий от filosofia

list-initialization limits the allowed implicit conversions by prohibiting the following:

  • conversion from a floating-point type to an integer type

  • conversion from a long double to double or to float and >conversion from double to float, except where the source is >a constant expression and overflow does not occur

  • conversion from an integer type to a floating-point type, >except where the source is a constant expression whose value can >be stored exactly in the target type

  • conversion from integer or unscoped enumeration type to integer type that cannot represent all values of the original, except where source is a constant expression whose value can be stored exactly in the target type

  • conversion from a pointer type or pointer-to-member type to bool

https://en.cppreference.com/w/cpp/language/list_initialization

fsb4000 ★★★★★
()
Ответ на: комментарий от JaM

int в bool тоже преобразуется на ура, но st.emplace(0) не компилируется.

Это потому что вызов неоднозначен (ambiguous). Закомментируй один из конструкторов и все будет компилироваться.

Ты не понимаешь механизм работы спецификатора explicit. По идее первая реакция должна быть «может я что-то делаю неправильно», а не «в компиляторе баг».

explicit не для того, чтобы запретить неявное преобразование типов ПЕРЕДАННЫХ аргументов в типы, которые УКАЗАНЫ В ОБЪЯВЛЕНИИ конструктора. А для того, чтобы запретить неявное преобразование типа аргумента к типу класса в случаях когда у конструктора один аргумент или не один но тогда у всех после первого есть значения по умолчанию.

struct Node {
  double r = 0;
  explicit Node(double rr) : r(rr) {}
};

int main() {
  Node n = 5.5; // ошибка
  
  Node n2(5.5);  // норм
  Node n3(5);    // норм
  Node n4(true); // норм

  // без explicit
  // Node n = 5.5; // норм
  // Node n2 = 5;  // норм
}
rumgot ★★★★★
()
Ответ на: комментарий от JaM

Ага, только при этом Node i{0}; благополучно выдаёт ошибку, так как никакого неявного преобразования не производится.

Да нет блин. Там получается неоднозначный вызов. Закомментируй один из конструкторов и будет компилироваться.

rumgot ★★★★★
()
Ответ на: комментарий от rumgot

Да, ты прав, я поспешил, не прочитал сообщение об ошибке. emplace делает direct initialization, где narrowing conversions не запрещены. Хорошо. Но проблема в компиляторе всё равно есть, поскольку он не выдаёт предупреждения об этом самом narrowing conversion.

JaM
() автор топика
Ответ на: комментарий от rumgot

Да, уже понял. Вот такое не должно компилироваться даже с одним конструктором:

int i;
Node n{i};

Clang не компилирует, gcc компилирует, правда, ворнинг всё же даёт.

JaM
() автор топика
Ответ на: комментарий от JaM

Это можно как-то побороть?

Используй strong typedefs и будет тебе счастье.

PRN
()

Как уже заметили - emplace ведь напрямую пробрасывает аргументы в конструктор c круглыми скобками, так что explicit тут не поможет. Контрпример - list<QQ>, QQ не умеет перемещаться и копироваться и имеет explicit конструктор, как его в листе создать?

По поводу замены в stl круглы скобок на фигурные - вряд ли, там ведь траблы могут быть с типами, принимающими initializer_list в конструкторе. Скорее () инициализацию до ума доводят - раньше нельзя было делать emplace для агрегатов, начиная с 20 можно, только там с лайфтаймом временный объектов при инициализации ссылок как-то странно …

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от kvpfs

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

Да, разработчики STL мне указали на тоже самое.

I don’t think so - the difference matters for vector. vector(11, -20) is 11 elements of value -20, vector{11, -20} is 2 elements

fsb4000 ★★★★★
()
Ответ на: комментарий от JaM

Это можно как-то побороть?

struct Node
{
 double r=0;
 explicit Node(double rr): r(rr) {}
 explicit Node(bool bb): r(-1.0) {}
 template <typename T>
	 Node(T *t) = delete;
};
kvpfs ★★
()
Ответ на: комментарий от JaM

Говорят надо нанять нормальных C++ разработчиков, которые осилили стандарт прочитать.

peregrine ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.