web-dev-qa-db-fra.com

Quand les informations de type sont-elles renvoyées en C ++?

Je viens de regarder Stephan T. Lavavej parler à CppCon 2018 sur "Déduction d'argument de modèle de classe", où à n point il dit accessoirement:

En C++, l'information de type ne coule presque jamais en arrière ... Je devais dire "presque" car il y a un ou deux cas, peut-être plus mais très peu .

En dépit d'essayer de comprendre à quels cas il pourrait faire référence, je n'ai rien pu trouver. D'où la question:

Dans quels cas la norme C++ 17 impose-t-elle que les informations de type se propagent en arrière?

90
Massimiliano

Voici au moins un cas:

struct foo {
  template<class T>
  operator T() const {
    std::cout << sizeof(T) << "\n";
    return {};
  }
};

si vous faites foo f; int x = f; double y = f;, les informations de type vont circuler "en arrière" pour déterminer ce que T se trouve dans operator T.

Vous pouvez l'utiliser de manière plus avancée:

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

alors maintenant je peux faire

std::vector<int> v = construct_from( 1, 2, 3 );

et il fonctionne.

Bien sûr, pourquoi ne pas simplement faire {1,2,3}? Eh bien, {1,2,3} n'est pas une expression.

std::vector<std::vector<int>> v;
v.emplace_back( construct_from(1,2,3) );

ce qui, certes, nécessite un peu plus de magie: Exemple en direct . (Je dois faire le retour de déduction faire un chèque SFINAE de F, puis faire le F être amical SFINAE, et Je dois bloquer std :: initializer_list dans l'opérateur déduit_retour T.)

79

Stephan T. Lavavej a expliqué le cas dont il parlait dans un Tweet :

Le cas auquel je pensais est celui où vous pouvez prendre l’adresse d’une fonction surchargée/basée sur un modèle et si elle est utilisée pour initialiser une variable d’un type spécifique, elle ne sera pas ambiguë. (Il y a une liste de ce qui est ambiguë.)

nous pouvons en voir des exemples depuis page cppreference sur l’adresse de la fonction surchargée , j’en ai excepté quelques-uns ci-dessous:

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

Michael Park ajoute :

Cela ne se limite pas à l'initialisation d'un type concret non plus. Il pourrait également déduire simplement du nombre d'arguments

et fournit cet exemple en direct :

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);
}

que j'élabore un peu plus ici .

30
Shafik Yaghmour

Je crois que dans la distribution statique de fonctions surchargées, le flux va en sens inverse de celui de la résolution de surcharge habituelle. Donc, l’un de ceux-là est à l’arrière, je suppose.

19
jbapple