Pertanyaan Apakah mungkin untuk mendapatkan alamat implantasi implisit dari template fungsi?


Saya ingin tahu apakah ada cara untuk mendapatkan alamat dari Instansiasi template fungsi yang dihasilkan oleh seperangkat argumen spesifik.

#include <iostream>

template <typename T>
class A {}; 

template <typename T, typename T2>
int hello(A<T> a, A<T2> b, int c)
{
    return 69; 
}

int main()
{
    A<int> a;
    A<float> b;
    std::cout << (&hello)(a, b, 3) << "\n";                                                                                                                                                           

    return 0;
}

Kode ini mencetak nilai yang dikembalikan oleh pemanggilan fungsi. Bagaimana saya bisa mencetak alamat versi "hello" yang dipakai untuk parameter a dan b? Saya ingin jenis yang akan disimpulkan oleh kompilator.


11
2018-04-08 18:39


asal


Jawaban:


Proses penentuan fungsi untuk memanggil berdasarkan argumen disebut resolusi overload, dan daftar standar di mana kasus itu digunakan:

13.3 Resolusi kelebihan beban [over.match]

2 Resolusi kelebihan beban memilih fungsi untuk memanggil dalam tujuh konteks berbeda dalam bahasa:

(2.1) - permintaan fungsi yang disebut dalam sintaks panggilan fungsi (13.3.1.1.1);

(2.2) - permintaan operator panggilan fungsi, fungsi konversi penunjuk-ke-fungsi, fungsi konversi referensi-ke-pointer-ke-fungsi, atau fungsi konversi referensi-ke-fungsi pada objek kelas bernama   dalam sintaks panggilan fungsi (13.3.1.1.2);

(2.3) - permintaan operator yang direferensikan dalam sebuah ekspresi (13.3.1.2);

(2.4) - permintaan konstruktor untuk inisialisasi langsung (8.5) dari objek kelas (13.3.1.3);

(2.5) - doa dari konversi yang ditentukan pengguna untuk copy-inisialisasi (8.5) dari objek kelas (13.3.1.4);

(2.6) - permintaan fungsi konversi untuk inisialisasi objek dari jenis nonclass dari ekspresi tipe kelas (13.3.1.5); dan

(2.7) - permintaan fungsi konversi untuk konversi ke nilai atau nilai kelas yang mana referensi (8.5.3) akan langsung terikat (13.3.1.6).

Dari jumlah ini, hanya salah satu yang berlaku untuk fungsi biasa adalah 2,1, dan yang membutuhkan f(args) konteks yang hanya memberi tahu si penelepon hasilnya.

Jadi, apa yang Anda minta tidak bisa dilakukan. Tidak juga sih.

Sekarang, tergantung pada apa yang ingin Anda capai, ada beberapa hal itu adalah mungkin:

Adalah mungkin untuk mendapatkan pointer ke fungsi jika Anda tahu tepat tanda tangan: diberikan template <typename T> int hello(A<T> a, A<T> b), Anda dapat memperoleh alamat menggunakan itu: static_cast<int(*)(A<int>,A<int>)>(hello). Namun, agar ini berfungsi, Anda perlu mengetahui jenis pengembalian (yang mungkin bisa Anda gunakan decltype), dan Anda perlu mengetahui jenis parameter (yang mungkin berbeda dari jenis argumen, dan yang tidak dapat Anda dapatkan dengan andal).

Juga dimungkinkan untuk mendapatkan penunjuk ke fungsi yang, saat dipanggil, akan memiliki efek yang sama hello:

auto callable = +[](A<int> a, A<int> b) { return hello(a, b); };

Itu [](A<int> a, A<int> b) { return hello(a, b); } membuat lambda tanpa menangkap apa pun, dan lambdas tanpa pengambilan apa pun dapat secara implisit dikonversi ke fungsi penunjuk dari jenis pencocokan. Itu + memaksa penggunaan konversi itu, tanpa mengharuskan jenisnya dieja.

Namun, ini tidak akan memiliki alamat yang sama hello, jadi mungkin tidak cocok untuk perbandingan selanjutnya.

Itu yang terbaik yang bisa Anda dapatkan.


8
2018-04-08 18:55



Saya mencoba ini Ideone:

// A and hello defined as OP
int main()
{
    A<int> a;
    A<float> b;

    decltype(hello(a,b,3)) (*pf)(decltype(a), decltype(b), decltype(3))=hello;
    std::cout << (void*)pf << "\n";                                                                                                                                                           

    return 0;
}

Tampaknya menghasilkan alamat memori.


2
2018-04-08 19:51



@hvd: AFAIK, Anda tidak dapat mendeklarasikan lambda tanpa mengetahui tanda tangan dari fungsi yang ingin Anda bungkus (lambdas tidak bisa templated). Tetapi Anda dapat menggunakan kelas menengah dengan metode statis sebagai gantinya:

#include <functional>
#include <iostream>

template<class A>
void func(A a, int b)
{
    std::cout << "a=" << a << " b=" << b << "\n";
}

template<class... A>
class proxy_func
{
public:
    static auto call(A... args) -> decltype(func(args...))
        {
            return func(args...);
        }
};

template<template<class...> class P, class... User>
void* addr(User... user)
{
    return (void*)&P<User...>::call;
}

template<template<class...> class P, class... User>
auto call(User... user) -> decltype(P<User...>::call(user...))
{
    return P<User...>::call(user...);
}

template<class T>
void test()
{
    T value = 1;
    printf("func > %p\n", &func<T>);
    printf("func > ");
    func(value, 1);
    printf("proxy> %p\n", &proxy_func<T, int>::call);
    printf("proxy> ");
    proxy_func<T, int>::call(value, 1);
    printf("auto > %p\n", addr<proxy_func>(value, 1));
    printf("auto > ");
    call<proxy_func>(value, 1);
}

int main(int argc, char **argv)
{
    printf("==int==\n");
    test<int>();
    printf("==long==\n");
    test<long>();
}

Hasilnya adalah ini:

g++ -std=c++11 -o /tmp/test /tmp/test.cpp && /tmp/test
==int==
func > 0x400a8d
func > a=1 b=1
proxy> 0x400ae6
proxy> a=1 b=1
auto > 0x400ae6
auto > a=1 b=1
==long==
func > 0x400b35
func > a=1 b=1
proxy> 0x400b91
proxy> a=1 b=1
auto > 0x400b91
auto > a=1 b=1

Tentu saja, ini membutuhkan pernyataan proksi generik yang mengetahui tentang nama fungsi target (tidak ada yang lain), dan itu mungkin tidak dapat diterima sebagai solusi untuk @ JavierCabezasRodríguez.

PS: Maaf saya tidak mempostingnya sebagai komentar, tapi saya tidak punya reputasi yang cukup untuk itu.

Edit

Menggunakan kelas proxy dan bukannya lambda, Anda perlu mengetahui jumlah parameter, sehingga Anda dapat menggunakan pendekatan makro yang curang untuk membungkus fungsi target apa pun:

#define proxy(f)                                \
    template<class... A>                        \
    class proxy_ ## f                           \
    {                                           \
    public:                                                     \
        static auto call(A... args) -> decltype(f(args...))     \
        {                                                       \
            return f(args...);                                  \
        }                                                       \
    }

proxy(func);

1
2018-04-09 12:45