Я только что наблюдал, как Стефан Т. Лававедж выступал CppCon 2018
на « Выведении аргументов из шаблона класса», где в какой-то момент он, между прочим, сказал:
В типе C ++ информация почти никогда не течет в обратном направлении ... Мне пришлось сказать «почти», потому что есть один или два случая, возможно, больше, но очень мало .
Несмотря на попытки выяснить, о каких случаях он может иметь в виду, я ничего не мог придумать. Отсюда вопрос:
В каких случаях стандарт C ++ 17 требует, чтобы информация о типах передавалась в обратном направлении?
c++
types
language-lawyer
c++17
type-deduction
Массимилиано
источник
источник
Ответы:
Вот хотя бы один случай:
struct foo { template<class T> operator T() const { std::cout << sizeof(T) << "\n"; return {}; } };
если вы это сделаете
foo f; int x = f; double y = f;
, информация о типе будет течь «в обратном направлении», чтобы выяснить, чтоT
находится внутриoperator T
.Вы можете использовать это более продвинутым способом:
template<class T> struct tag_t {using type=T;}; template<class F> struct deduce_return_t { F f; template<class T> operator T()&&{ return std::forward<F>(f)(tag_t<T>{}); } }; template<class F> deduce_return_t(F&&)->deduce_return_t<F>; template<class...Args> auto construct_from( Args&&... args ) { return deduce_return_t{ [&](auto ret){ using R=typename decltype(ret)::type; return R{ std::forward<Args>(args)... }; }}; }
так что теперь я могу сделать
std::vector<int> v = construct_from( 1, 2, 3 );
и это работает.
Конечно, а почему бы просто не сделать
{1,2,3}
? Ну,{1,2,3}
это не выражение.std::vector<std::vector<int>> v; v.emplace_back( construct_from(1,2,3) );
что, по общему признанию, требует немного больше волшебства: Живой пример . (Мне нужно, чтобы вывод вывода выполнял проверку SFINAE для F, затем сделать F дружественным к SFINAE, и мне нужно заблокировать std :: initializer_list в операторе deduce_return_t T.)
источник
&&
Отборочные наoperator T()
это большой сенсорный; это помогает избежать плохого взаимодействия сauto
, вызывая ошибку компиляции, еслиauto
здесь неправильно используется.Стефан Т. Лававей объяснил случай, о котором он говорил, в твите :
мы можем увидеть примеры этого на странице cppreference в Адресе перегруженной функции , я исключил несколько ниже:
int f(int) { return 1; } int f(double) { return 2; } void g( int(&f1)(int), int(*f2)(double) ) {} int main(){ g(f, f); // selects int f(int) for the 1st argument // and int f(double) for the second auto foo = []() -> int (*)(int) { return f; // selects int f(int) }; auto p = static_cast<int(*)(int)>(f); // selects int f(int) }
Майкл Парк добавляет :
и предоставляет этот живой пример :
void overload(int, int) {} void overload(int, int, int) {} template <typename T1, typename T2, typename A1, typename A2> void f(void (*)(T1, T2), A1&&, A2&&) {} template <typename T1, typename T2, typename T3, typename A1, typename A2, typename A3> void f(void (*)(T1, T2, T3), A1&&, A2&&, A3&&) {} int main () { f(&overload, 1, 2); }
о котором я подробнее расскажу здесь .
источник
Я верю в статическое приведение перегруженных функций, поток идет в противоположном направлении, как при обычном разрешении перегрузки. Думаю, один из них - наоборот.
источник