c ++ 11 Завершающий тип возврата с decltype не работает должным образом - PullRequest
1 голос
/ 27 января 2020

Почему это работает "как положено"? Насколько я понимаю, это не должно работать:

template <class T, class U>
auto x(T a, U b) -> decltype(a<b ? a:b) {
    return a > b ? a : b;
}


int main() {
    cout << x<long, double>(1, 2.01) << endl;
    cout << x<long, double>(5, 2.01) << endl;

}

Я пробовал некоторые другие комбинации, такие как:

template <class T, class U>
auto x(T a, U b) -> decltype(a<b ? a:a) {
    return a > b ? a : b;
}

Таким образом, он не компилируется с ошибкой. На самом деле вторая комбинация не работает с

compile time error:  Error C2440 'return': cannot convert from 'U' to 'T &' 

, что ожидается. Насколько я понимаю, первая функция также должна завершиться с той же ошибкой, в то время как она работает нормально.

Ответы [ 2 ]

3 голосов
/ 27 января 2020

Неважно, каково условие с оператором ?:. Тип результата вычисляется как общий тип второго и третьего операнда. Вот часть того, как вычисляется общий тип и категория значений оператора ?:, для получения полной информации см. cppreference.com :

Если второй и третий операнд являются lvalue одного и того же типа, тогда тип результата будет lvalue этого типа.

Если типы являются несвязанными lvalues, существуют некоторые более сложные правила для определения общего типа, но результатом будет prvalue, а не lvalue. В частности, если двумя типами являются арифметические c типы , такие как double и long, то применяются обычные арифметические c преобразования чтобы получить общий тип. В случае long и double этот общий тип будет double. Это то же самое вычисление типа, которое будет выполнено, если вы, например, попытаетесь добавить два разных арифметических типа c с +, отсюда и название обычная арифметическая c конверсия .

Следовательно, decltype(a<b ? a:b) будет ссылочным типом, если a и b имеют одинаковый тип, в противном случае он не будет ссылочным типом.

Именно поэтому функция компилируется. Общий тип всегда таков, что оба типа ввода могут быть преобразованы в него. Именно поэтому функция имеет неопределенное поведение, если типы равны, потому что тогда decltype дает ссылку, и поэтому вы собираетесь вернуть ссылку на один из параметров функции.

decltype(a<b ? a:a) didn ' t работают с разными типами, потому что общий тип a и a, как описано выше, является ссылкой на тип a. Если b имеет другой несвязанный тип, результатом a > b ? a : b будет prvalue, который нельзя привязать к ссылке lvalue.

1 голос
/ 27 января 2020

Вы можете подумать, что

template <class T, class U>
auto x(T a, U b) -> decltype(a < b ? a : b) {
    return a > b ? a : b;
}

std::cout << x<long, double>(1, 2.01) << std::endl;
std::cout << x<long, double>(5, 2.01) << std::endl;

не скомпилируется, потому что конечный тип возврата decltype(a < b ? a : b), то есть внутреннее выражение a < b ? a : b не совпадает с возвращаемым выражением a > b ? a : b, но это совершенно законно поскольку возвращаемый тип становится любым типом, следующим после ->.

Теперь, почему следующий код не скомпилирован успешно?

template <class T, class U>
auto x(T a, U b) -> decltype(a < b ? a : a) {
    return a > b ? a : b;
}

std::cout << x<long, double>(1, 2.01) << std::endl;
std::cout << x<long, double>(5, 2.01) << std::endl;

Это потому, что вам нужно удалить ссылку из конечный тип возврата, например:

#include <type_traits>

template <class T, class U>
auto x(T a, U b) -> typename std::remove_reference<decltype(a < b ? a : a)>::type {
    return (a > b ? a : b);
}

Теперь он успешно компилируется. Проверьте здесь

Причина, по которой второй пример не компилируется без std::remove_reference, заключается в том, что оба типа, являющиеся частью тернарного оператора, являются glvalue s и здесь говорят, что:

4) Если E2 и E3 являются glvalues ​​одного и того же типа и той же категории значения, то результат имеет тот же тип и категорию значения

, поэтому результат a < b ? a : a равен glvalue. Вот почему вам нужно удалить ссылку. И результат a < b ? a : b равен prvalue, поэтому вам не нужно удалять ссылку.

...