Почему std :: overload вместо concept / constexpr, если в std :: visit? - PullRequest
2 голосов
/ 06 апреля 2019

Я могу быть немного озадачен предложением std :: overload и / или концепциями, но с моим текущим пониманием обоих вопросов у меня возникает следующий вопрос:

Почему C ++ 20 не просто концептуализирует / если constexprify std :: visit, поэтому он знает, что делать, основываясь на типах передаваемых ему аргументов.

Например, почему у нас не может быть std :: visit, который изменяет его поведение, основываясь на понятиях передаваемых аргументов (все, что требуется, это чтобы функции шли первыми, а варианты последними). ​​

Так, например, оба эти visit принимают 3 аргумента, но имеют разную логику.

    std::variant<int, double> v1=4.7;
    std::variant<bool, std::string> v2=false;
    // calls lambda that is a better fit(double one)
    std::visit([](int& a){std::cout<< sizeof (a);},[](double& a){std::cout << a;} , v1); 
    // calls lambda on variant v1 and then variant v2
    std::visit([](auto& a){}, v1, v2); 

1 Ответ

5 голосов
/ 06 апреля 2019

Я думаю, это была бы очень плохая идея.

visit сейчас концептуально простая функция. Это делает это:

template <typename F, typename... Variants>
decltype(auto) visit(F f, Variants... vs) {
    return f(std::get<vs.index()>(vs)...);
}

За исключением, конечно, vs.index() не является константным выражением, поэтому вы не можете просто сделать это, вам нужна вся эта сложная реализация, чтобы это работало. Но дело в том, что * visit группа вариантов просто вызывает f на текущих альтернативах каждого из них. Это просто, чтобы рассуждать о.

Наличие общего алгоритма для семантически разных вещей, основанных на типах, которые он передает, очень плохая идея. Это означает, что вы не можете на самом деле рассуждать о правильности своего кода - потому что он может делать что угодно Последний пример в OP является хорошим примером этого:

std::visit([](auto&){}, v1, v2);        // #1
std::visit([](auto&, auto&){}, v1, v2); // #2

Сегодня #1 (пример OP) не компилируется, потому что вы передаете унарную функцию двоичному посетителю. Это хорошая вещь. Но с предложением, оба скомпилируют и делают дико разные вещи. Один посетил бы каждый вариант последовательно, другой посетил бы каждый вариант вместе.

Каковы были намерения пользователя? Может быть, #1 было ошибкой, и пользователь либо забыл аргумент, либо предоставил посетителя, который работал в качестве бинарного посетителя в большинстве, но не во всех случаях, но работает как унарный посетитель во всех случаях?

<ч />

Другой пример в OP менее плох, который имеет:

visit(f1, f2, f3, v1, v2, v3)

означает троичное посещение по трем комбинированным функциям, а не квазинарное посещение по одной функции:

visit(overload(f1, f2, f3), v1, v2, v3)

Но это было бы очень сложно реализовать, с минимальным усилением (пользователь мог бы просто обернуть функции на своей стороне, верно?), И вы должны начать задавать вопросы типа: что, если у вас есть вызываемый variant?

Если вы собираетесь использовать другой синтаксис, я видел несколько примеров кода, где люди осуществляют посещение синтаксически следующим образом:

visit2(v1, v2, v3)(f1, f2, f3)

Это очень просто реализовать:

template <typename... Variants>
auto visit2(Variants&&... variants) {
    return [&](auto&&... fs) -> decltype(auto) {
        return std::visit(overload(FWD(fs)...), FWD(variants)...);
    };
}

И имеет преимущество в том, что сначала ставится вариант (ы) и не нужно писать overload. Может быть, этого достаточно.

...