When does type information flow backwards in C++?

前端 未结 3 1593
栀梦
栀梦 2021-01-29 23:39

I just watched Stephan T. Lavavej talk at CppCon 2018 on \"Class Template Argument Deduction\", where at some point he incidentally says:

In

相关标签:
3条回答
  • 2021-01-29 23:47

    Here is at least one case:

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

    if you do foo f; int x = f; double y = f;, type information will flow "backwards" to figure out what T is in operator T.

    You can use this in a more advanced way:

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

    so now I can do

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

    and it works.

    Of course, why not just do {1,2,3}? Well, {1,2,3} isn't an expression.

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

    which, admittedly, require a bit more wizardry: Live example. (I have to make the deduce return do a SFINAE check of F, then make the F be SFINAE friendly, and I have to block std::initializer_list in deduce_return_t operator T.)

    0 讨论(0)
  • 2021-01-29 23:56

    Stephan T. Lavavej explained the case he was talking about in a tweet:

    The case I was thinking of is where you can take the address of an overloaded/templated function and if it’s being used to initialize a variable of a specific type, that will disambiguate which one you want. (There’s a list of what disambiguates.)

    we can see examples of this from cppreference page on Address of overloaded function, I have excepted a few below:

    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 adds:

    It's not limited to initializing a concrete type, either. It could also infer just from the number of arguments

    and provides this live example:

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

    which I elaborate a little more here.

    0 讨论(0)
  • 2021-01-29 23:57

    I believe in static casting of overloaded functions the flow goes the opposite direction as in usual overload resolution. So one of those is backwards, I guess.

    0 讨论(0)
提交回复
热议问题