Блог пользователя Kvark161

Автор Kvark161, 10 лет назад, По-русски

lunawyll наткнулась на прикольное поведение c++11. Попробуйте угадать, что будет выведено, если использовать c++11?

#include <set>
#include <iostream>

using namespace std;

struct S {
    int a;
    int b;
    
    S(int a = 0, int b = 0): a(a), b(b) {};

    friend bool operator < (const S& first, const S& second) {
        return first.a < second.a;
    }
};

int main() {
    set<S> s;
    s.insert({2, 1});
    cout << s.size();
}

Неожиданный результат, но в сет будет добавлено 2 элемента (a=2, b=0) и (a=1, b=0).

Почему это так? Дело в том, что в c++11 был добавлен еще один метод void insert (initializer_list<value_type> il);. Получается, что {2, 1} преобразуется в initializer_list<S> с двумя элементами (a=2, b=0) и (a=1, b=0) с помощью неявного преобразования int в S, используя конструктор, который мы описали.

GNU g++ 4.9.2 (не c++11): в сете будет один элемент (a=2, b=1).

MS VC++ 2010: ошибка компиляции.

Всем добра! :)

P.S. Каким-то образом я умудрился удалить пост, написал его заного...

UPD: О том, как я удалил пост. В английской версии сайта при редактировании поста есть кнопочка "discard" — это разве не переводится как "отмена"? Оказалось — "удалить".

  • Проголосовать: нравится
  • +65
  • Проголосовать: не нравится

»
10 лет назад, # |
  Проголосовать: нравится +35 Проголосовать: не нравится

Ну потому что надо explicit перед такими конструкторами писать по хорошему.

»
10 лет назад, # |
  Проголосовать: нравится +8 Проголосовать: не нравится

В C++11 контейнерах в подобных случаях намного полезнее писать не s.insert({2, 1}), а s.emplace(2, 1). И подобной ошибки не будет, и параметры будут perfect forward-иться.

К тому же, все еще считаю, что параметры по умолчанию — зло. Хотя они и немного сокращают количество кода, но количество обидных опечаток, не заметных на этапе компиляции, значительно увеличивают.

»
10 лет назад, # |
  Проголосовать: нравится +14 Проголосовать: не нравится

С минимальными изменениями и чтобы работало можно написать

s.insert(S{2, 1});

Но от

S tmp(3); // я так понимаю, что такое использование не желательно

explicit не спасёт.

А вообще, и правда, обычно пишут

S() : a(0), b(0) {}
S(int a, int b) : a(a), b(b) {}
  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится +3 Проголосовать: не нравится

    Так, конечно, делать хорошо. Но во время контеста не хочется писать лишних строк.

    Минимальные изменения — это удалить конструктор и спокойно писать:

    s.insert({2, 1});
    
    • »
      »
      »
      10 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Удалить можно, но тогда не будут поля нулями инициализироваться (если это не нужно, то непонятно, зачем вообще изначально конструктор был нужен).

      • »
        »
        »
        »
        10 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        В данном случае он вообще не нужен, да и в том коде, в котором наткнулись на такую неприятнось, тоже был не нужен, но он был.

      • »
        »
        »
        »
        10 лет назад, # ^ |
        Rev. 2   Проголосовать: нравится +16 Проголосовать: не нравится

        В с++0x можно писать:

        struct C {
           C(int a, int b): a(a), b(b){}
           int a = 0;
           int b = 0;
        };
        
»
10 лет назад, # |
Rev. 2   Проголосовать: нравится -20 Проголосовать: не нравится

Говоря об выстрелах в свои конечности, недавно столкнулся с забавной проблемой в поведении кода. Конкретно проблема состоит в том, что срабатывает данный ассерт :
~~~~~

sort(a.begin(), a.end(), cmp1);

for (int i = 1; i < a.size(); i++) {
    assert(cmp1(a[i - 1], a[i]));
}

~~~~~
upd. да, таки бред (\/)(*,,,*)(\/)

»
10 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Вроде как в C++98 это вообще не должно компилироваться.

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Капитан в треде :)

    • »
      »
      »
      10 лет назад, # ^ |
      Rev. 3   Проголосовать: нравится +3 Проголосовать: не нравится

      Ну, там ближе к концу написано, что в C++98 всё работает как ожидалось. Оно вообще не должно не то что работать, а даже компилироваться — это GCC слишком добрый (хотя и выдаёт предупреждение).