Создание шаблонов функций-членов для нескольких типов - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть несколько классов с шаблонными функциями-членами и предопределенным списком типов, с которыми они будут использоваться ( Wandbox link :

// main.cpp:
#include "class.h"

int main(int argc, char** argv) {
    A a;
    a.foo(5);
    a.foo(5.);
    B b;
    //b.bar(1,2,3,4); b.bar(1,2,3.,4);
    return 0;
}

// class.h
#pragma once

struct A {
    template<typename T> void foo(T x);
};
struct B {
    template<typename T> void bar(int p1, int p2, T x, int p3);
};


// class.cpp
#include <iostream>
#include "class.h"

template<typename T> void A::foo(T x) {
    std::cout << x << std::endl;
}

// explicit, but very verbose
// template void A::foo(int);
// ...

template<typename T> void ignore(T fn) {/* Use fn? */}

template<class Class>
void instantiate(Class) {
    // List all types the function should be instantiated for here
    ignore(&Class::template foo<int>);
    ignore(&Class::template foo<double>);
}

// works, instantiates A::foo<int> and A::foo<double>
template void instantiate(A);
// How to pass B::foo and additional parameters?
// template void instantiate(B);

Ввод каждой комбинации функции-типа и типа для создания экземпляров с работами, но имеет несколько недостатков:

  • это утомительно
  • должна быть напечатана вся сигнатура функции с каждым параметром, а
  • изменения подписи функции должны быть сделаны в нескольких местах
  • добавление одного типа в список должно быть сделано для каждой функции-члена

Мой обходной путь, как описано выше, работает на большинстве старых компиляторов, которые я тестировал (совместимость с C ++ 03 была бы огромным плюсом), но я не уверен, что смарт-компилятору разрешат удалить неиспользуемый параметр и функцию конкретизации.

Для обычных функций есть обходных путей , но, насколько я понял, они не работают для функций-членов.

Как я могу изменить свою функцию instantiate, чтобы она также принимала функцию-член и дополнительные параметры?

1 Ответ

0 голосов
/ 05 сентября 2018

После долгих попыток и ошибок я нашел что-то, что работает даже при включенной оптимизации ( Wandbox ):


// main.cpp as above

// class.h
#pragma once

// Create instantiations for templated functions by keeping their addresses
// and therefore forcing the compiler to keep their object code
// attribute((unused)) silences the clang/gcc warning
template <typename T> void ignore(T t) {static __attribute__((used)) T x = t;}

struct A {
    template<typename T> void foo(T x);
    template<typename T> friend void instantiate(T); // let instantiate call the helper function in case it's private
    // helper function that instantiates all member functions
    template<typename T> void helper() { ignore(&A::foo<T>); }
};
struct B {
    template<typename T> void bar(int p1, int p2, T x, int p3);
    // same procedure as above
    template<typename T> friend void instantiate(T);
    template<typename T> void helper() { ignore(&B::bar<T>); }
};


// class.cpp
#include 
#include "class.h"

template void A::foo(T x) {
    std::cout  void B::bar(int, int, T, int) {}

template
void instantiate(Class) {
    // List all types the function should be instantiated for here
    ignore(&Class::template helper);
    ignore(&Class::template helper);
}

template void instantiate(A);
template void instantiate(B);

Чтобы избежать более сложной магии шаблонов, я добавил одну функцию шаблона (template<typename T> void helper()), для которой создается функция instantiate, в которой перечислены все нужные типы для функций, для которых создаются экземпляры.

После этого template void instantiate(A) создаст экземпляры всех функций-членов, перечисленных в A::helper.

...