Когда информация о типе возвращается в C ++? - PullRequest
0 голосов
/ 13 ноября 2018

Я только что видел, как Стефан Т. Лававей говорил в CppCon 2018 о «Удержании аргумента шаблона класса», где в некоторый момент он, кстати, говорит:

В типе C ++ информация почти никогда не течет в обратном направлении ... Мне пришлось сказать "почти", потому что есть один или два случая, возможно, больше, но очень мало .

Несмотря на попытку выяснить, на какие случаи он может ссылаться, я ничего не смог придумать. Отсюда вопрос:

В каких случаях стандарт C ++ 17 предписывает, чтобы информация о типах распространялась в обратном направлении?

Ответы [ 3 ]

0 голосов
/ 13 ноября 2018

Вот как минимум один случай:

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.)

0 голосов
/ 13 ноября 2018

Стефан Т. Лававей объяснил случай, о котором он говорил, в твиттере :

Случай, о котором я думал, - это когда вы можете взять адрес перегруженной / шаблонной функции и, если она используется для инициализации переменной определенного типа, это будет однозначно определять, какой из них вы хотите. (Есть список того, что неоднозначно.)

мы можем видеть примеры этого со страницы 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);
}

который я немного уточню подробнее здесь .

0 голосов
/ 13 ноября 2018

Я считаю, что при статическом приведении перегруженных функций поток идет в противоположном направлении, как при обычном разрешении перегрузки. Так что, я думаю, один из них задом наперед.

...