Почему std:: is_assignable counter-intuive?

std::is_assignable<int, int>::value == false в соответствующей реализации (например, clang/libС++, gcc/libstdС++, но не VS2012).

Интуитивно это означает, что выражение, такое как int x = 3;, недействительно. Но в спецификации is_assignable указано, что обе стороны присваивания преобразуются в std::add_rvalue_reference<T>::type, поэтому std::is_assignable<int, int>::value должна оцениваться до false (потому что int + &&int&&, что является неприменимое значение r).

Почему дизайн std::is_assignable разработан таким образом, или я не понимаю что-то о том, что действительно означает is_assignable<int, int>::value?

Связанные дискуссиях:

Ответ 1

В этих чертах и ​​ T не является ссылочным типом lvalue, T подразумевает rvalue.

Со многими пользовательскими типами T вполне разумно назначать типы rvalue. И это даже очень полезно в некоторых контекстах:

std::vector<bool> v(5);
v[0] = true;

В приведенном выше выражении v[0] является значением r, которому присваивается значение. И если vector<bool> - плохой пример, то следующий новый код С++ 11 делает то же самое:

#include <tuple>

std::tuple<int, int>
do_something();

int
main()
{
    int i, j;
    std::tie(i, j) = do_something();
}

Выше, результат do_something() присваивается rvalue std::tuple. Присвоение значений r полезно и даже обычно, хотя и не выполнено в подавляющем большинстве применений присвоения.

Итак, std::is_assignable позволяет определить различие между возможностью присвоения значения r и lvalue. Если вам нужно знать разницу, std::is_assignable может выполнить вашу работу.

Если вы имеете дело с более распространенным случаем, например, просто пытаетесь выяснить, является ли тип T назначаемой копией или нет, используйте is_copy_assignable<T>. Эта черта буквально определяется в терминах is_assignable и заставляет lhs к lvalue:

is_copy_assignable<T> == is_assignable<T&, const T&>

Итак, std::is_copy_assignable<int>::value будет истинным, как ожидалось.

Используйте is_copy_assignable как ваш первый выбор, или is_move_assignable, если вам это нужно. Только тогда, когда эти черты не сработают для вас (возможно, потому, что вам нужно посмотреть на гетерогенное назначение), следует ли вернуться к использованию is_assignable напрямую. И тогда вам нужно решить вопрос о том, хотите ли вы разрешить rvalues ​​на lhs, чтобы учитывать случаи, которые могут включать в себя vector<bool>::reference или tuple ссылок. Вам нужно будет явно указать, хотите ли вы разрешить такие случаи в вашем запросе is_assignable.

Например:

#include <type_traits>
#include <vector>

int
main()
{
    static_assert(std::is_assignable<std::vector<bool>::reference&, bool>(),
        "Should be able to assign a bool to an lvalue vector<bool>::reference");

    static_assert(std::is_assignable<std::vector<bool>::reference, bool>(),
        "Should be able to assign a bool to an rvalue vector<bool>::reference");

    static_assert(std::is_assignable<bool&, std::vector<bool>::reference>(),
        "Should be able to assign a vector<bool>::reference to an lvalue bool");

    static_assert(!std::is_assignable<bool, std::vector<bool>::reference>(),
        "Should not be able to assign a vector<bool>::reference to an rvalue bool");
}

Ответ 2

std::is_assignable<int, int>::value == false означает, что "литерал int не может быть назначен литералу int" (среди прочего).

Ваше выражение int x = 3 является std::is_assignable<int&, int>::value.

Для получения дополнительной информации:http://en.cppreference.com/w/cpp/types/is_assignable

Ответ 3

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

Запрос std::is_assignable_t<T&,U> скажет вам, можете ли вы разумно написать такой код, как:

T t;
U u;
t = u;

Я говорю это прагматично, потому что если бы они написали std::is_assignable<> для использования встроенной ссылки lvalue слева, было бы сложнее повторно использовать эту альтернативу для получения ее текущей функциональности. Проще, если пользователь добавляет ссылку lvalue к произвольному типу снаружи (он не будет удален), чем требовать ссылку rvalue снаружи для другого запроса (который можно легко удалить).

Итак, эта версия std::is_assignable<> будет отвечать вашему типичному тесту присваивания "lvalue receive from rvalue", но также и более редким запросам.

Наказанием за эту гибкость является нелогичность, которую вы описали.