Семантика изменения ключевого слова const - PullRequest
0 голосов
/ 08 мая 2020

Я попытался сегодняшнее испытание leetcode на C ++. Вы должны найти кузенов в двоичном дереве. Вот мой код.

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

class Solution {
public:
    bool isCousins(TreeNode* root, int x, int y) {
        this->x = x;
        this->y = y;
        return visit(overloaded{
            [](const bool& r) { 
                return r; 
            },
            [](const optional<int>& r) { 
                return false; 
            }
        }, helper(root));
    }

private:
    int x;
    int y;

    variant<const bool, optional<int>> helper(TreeNode* node) {
        if (node == nullptr) {
            return variant<const bool, optional<int>>((optional<int>()));
        }
        else if (node->val == x) {
            return variant<const bool, optional<int>>(optional<int>(0));
        }
        else if (node->val == y) {
            return variant<const bool, optional<int>>(optional<int>(0));
        }
        else {
            auto l = helper(node -> left);
            auto r = helper(node -> right);
            return visit(overloaded{
                [](const bool& l, optional<int>& r) {
                    assert(!r.has_value());
                    return variant<const bool, optional<int>>(l); 
                },
                [](optional<int>& l, const bool& r) {
                    assert(!l.has_value());
                    return variant<const bool, optional<int>>(r); 
                },
                [](optional<int> l, optional<int> r) {
                    if (l.has_value() && r.has_value()) {
                        return variant<const bool, optional<int>>(*l > 0 && *l == *r);
                    }
                    else if (l.has_value()) { 
                        ++*l;
                        return variant<const bool, optional<int>>(l); 
                    }
                    else if (r.has_value()) { 
                        ++*r;
                        return variant<const bool, optional<int>>(r); 
                    }
                    else {
                        return variant<const bool, optional<int>>((optional<int>())); 
                    }
                }
            }, l, r);
        }
    }
};

Тестовый пример, демонстрирующий мою проблему:

[1,3,2,null,null,7,4,null,null,5,6,null,8,null,9]
8
9

Приведенный выше код выполняется и успешно завершается. Однако, если я удалю одно ключевое слово const в строке 10 ([](bool& r) {), оно вернет другой (неправильный) ответ. const предназначен для безопасности во время компиляции, поэтому не должен влиять на семантику, но я думаю, что-то странное происходит с перегрузкой const? Что именно происходит?

Возможно, связано: это также нарушает мою ментальную модель, согласно которой, если вместо объявления l, r в строках 34,35 я передаю их напрямую в качестве аргументов для посещения (ie. return visit(overloaded{..}, helper(node->left), helper(node->right)), тоже не получается. Я бы снова ожидал, что это не повлияет на семантику.

1 Ответ

5 голосов
/ 08 мая 2020

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

Рассмотрим следующий набор и вызов перегрузки:

void f(int const &) { std::cout << "i"; }
void f(bool) { std::cout << "b"; }

int main()
{
    int const i = 42;
    f(i); // prints i
    f(42); // prints i
}

оба вызывают первую функцию (как и ожидалось), потому что int const & привязывается к int const, а также int&& (временный int).

Однако, если мы удалим const в первой функции набора перегрузки и сделаем тот же вызов:

void f(int &) { std::cout << "i"; }
void f(bool) { std::cout << "b"; }

int main()
{
    int const i = 42;
    f(i); // prints b
    f(42); // prints b
}

первая функция не выбрана, потому что int & не может связываться с int const. Однако bool может связываться с int const (после неявного преобразования) и вызывает вторую функцию. Точно так же int & не может связываться с int&&, но bool может, поэтому он вызывает вторую функцию.

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

...