2012-04-18 2 views
1

Я передаю как подпись, так и фактический указатель функции-члена класса как параметры шаблона класса шаблона. Есть ли способ различной специализации такого класса для перегрузок операторов? Я попытался посмотреть в type_traits и получить подсказку от std::is_copy_assignable, но похоже, что g ++ использует встроенные модули (например, __has_trivial_copy).Можно ли проверить время компиляции, если функция-член является оператором?

+0

Вы хотите знать (a), что определенная функция-указатель-член является перегруженным оператором или (b), например. шаблонный аргумент A фактически реализует A * A? – jpalecek

+1

http://www.boost.org/doc/libs/1_49_0/libs/type_traits/doc/html/boost_typetraits/category/value_traits/operators.html – Anonymous

+0

(a). Я предполагаю, однако, что реализация должна будет проверить, что тип, к которому принадлежит указатель-член-функция, выполняет проверяемый оператор. –

ответ

0

Ну, я работал над этим вопросом.

Я думаю, что можно получить результат, но это довольно сложно, потому что класс А, который хочет знать, является ли один из его собственных аргументов шаблона оператором класса B, должен сначала проверить, что оператор B :: существует , затем проверьте, равен ли он аргументу шаблона. Это быстро увеличивает сложность задачи.

Кроме того, во время тестирования я обнаружил, что g ++ по-прежнему очень плохо поддерживает поддержку шаблонов. Например, этот код делает шаг 2, то предполагается, что определенная функция-член существует, проверьте, если аргумент шаблона равен одной и той же функции члена:

#include <iostream> 
using namespace std; 

struct Hello{ 
    int helloworld(){ 
     return 0; 
    } 
    int goodbyeworld(){ 
     return 0; 
    } 
}; 

template<typename T1, T1, typename T2, T2> struct is_same_method{ 
    static constexpr bool value=false; 
}; 

template<typename Return, typename Class, typename... Args, Return(Class::*member)(Args...)> 
struct is_same_method<Return(Class::*)(Args...), member, Return(Class::*)(Args...), member>{ 
    static constexpr bool value=true; 
}; 

#define method_test(a, b) is_same_method<decltype(a), a, decltype(b), b>::value 


template<typename T, T> struct what_am_I_passed; 

template<typename Return, typename Class, typename... Args, Return(Class::*member)(Args...)> 
struct what_am_I_passed<Return(Class::*)(Args...), member>{ 
    static void so_what(){ 
     /* 
     * error: ‘decltype (& Class:: helloworld)’ is not a valid type for a template constant parameter. 
     */ 
     cout<<"you passed me "<<(method_test(member, &Class::helloworld)?"helloworld":"something else")<<endl; 
    } 
}; 

int main(){ 
    what_am_I_passed<decltype(&Hello::helloworld), &Hello::helloworld>::so_what(); 
} 

Теперь этот код завершается с г ++ 4.4, 4.5, сбоев 4.6.1 и работает с 4.6.2.

После всех этих проблем я решил перенести часть логики во время выполнения. Это то, чем я оказался.

#include <iostream> 
#include <type_traits> 
using namespace std; 

template<typename mem_type, mem_type mem> struct operator_type{ 
    enum types{ 
     //complete me... 
     NONE=0, ADD, SUB, MUL, DIV, MOD, POW, UNM, EQ, NEQ, LT, LE, GT, GE, SUBSCRIPT, CALL 
    }; 
    static types what(){ return NONE; } 
}; 

typedef operator_type<int, 0>::types op_types; 

template<typename Return, typename Class, typename... Args, Return(Class::*mem)(Args...)> 
class operator_type<Return(Class::*)(Args...), mem>{ 

#define isOp(name, symbol, args)\ 
     template<typename Class_,int=0> static bool is##name(float&&){ return false; }\ 
     template<typename Class_, Return(Class_::*innermem)(Args...)=&Class_::operator symbol>\ 
     static bool is##name(int&&){ return innermem==mem && (args<0 || sizeof...(Args)==args); } 
#define testOp(name) if(is##name<Class>(0)) return op_types::name 

    //complete me... 
    isOp(ADD, +, 1) 
    isOp(SUB, -, 1) 
    isOp(MUL, *, 1) 
    isOp(DIV, /, 1) 
    isOp(MOD, %, 1) 
    isOp(POW, ^, 1) 
    isOp(UNM, -, 0) 
    isOp(EQ, ==, 1) 
    isOp(NEQ, !=, 1) 
    isOp(LT, <, 1) 
    isOp(LE, <=, 1) 
    isOp(GT, >, 1) 
    isOp(GE, >=, 1) 
    isOp(SUBSCRIPT, [], 1) 
    isOp(CALL,(), -1) 

public: 

    static op_types what(){ 
     //complete me... 
     testOp(ADD); 
     testOp(SUB); 
     testOp(MUL); 
     testOp(DIV); 
     testOp(MOD); 
     testOp(POW); 
     testOp(UNM); 
     testOp(EQ); 
     testOp(NEQ); 
     testOp(LT); 
     testOp(LE); 
     testOp(GT); 
     testOp(GE); 
     testOp(SUBSCRIPT); 
     testOp(CALL); 
     return op_types::NONE; 
    } 
}; 




template<typename T, T> struct wants_to_know_operators; 

template<typename Return, typename Class, typename... Args, Return(Class::*mem)(Args...)> 
struct wants_to_know_operators<Return(Class::*)(Args...), mem>{ 
    typedef operator_type<decltype(mem), mem> my_operator_type; 
    static void stuff(){ 
     switch(my_operator_type::what()){ 
     case op_types::NONE:  cout<<"this is not an operator"<<endl; break; 
     case op_types::CALL:  cout<<"this is operator()"<<endl; break; 
     case op_types::SUBSCRIPT: cout<<"this is operator[]"<<endl; break; 
     case op_types::SUB:   cout<<"this is operator-"<<endl; break; 
     case op_types::UNM:   cout<<"this is operator- (unary)"<<endl; break; 
     //complete me... 
     default: cout<<"something else..."<<endl; break; 
     } 
    } 
}; 

struct Test{ 
    void operator()(){ 

    } 
    Test& operator-(){ 
     return *this; 
    } 
    Test& operator-(int){ 
     return *this; 
    } 
    int operator[](int){ 
     return 0; 
    } 
    int operator[](iostream){ 
     return 0; 
    } 
    int operator==(int){ 
     return 0; 
    } 
    void f(){} 
}; 


int main(){ 
    wants_to_know_operators<decltype(&Test::f), &Test::f>::stuff(); 
    wants_to_know_operators<int(Test::*)(int), &Test::operator[]>::stuff(); 
    wants_to_know_operators<int(Test::*)(iostream), &Test::operator[]>::stuff(); 
    wants_to_know_operators<decltype(&Test::operator()), &Test::operator()>::stuff(); 
    wants_to_know_operators<decltype(&Test::operator==), &Test::operator==>::stuff(); 
    wants_to_know_operators<Test&(Test::*)(), &Test::operator- >::stuff(); 
    wants_to_know_operators<Test&(Test::*)(int), &Test::operator- >::stuff(); 
} 

Синтаксис несколько громоздкий, но это лучшее, что я мог бы разработать с помощью шаблонов. Обратите внимание, что он может различать разные перегрузки для одного и того же оператора. Для моей цели это достаточно и, возможно, даже предпочтительнее, потому что все дело в том, чтобы экспортировать C++-функцию в Lua, и, конечно же, вы не можете проталкивать вещи в стек Lua во время компиляции.

+0

Итак, ваш код работал в текущей версии компилятора, но вы говорите, что он «все еще имеет очень плохую поддержку» ... хм, интересные критерии у вас там есть. Может быть, вы имеете в виду старые версии gcc, которые предшествуют C++ 11 _had_ плохой поддержке этого незавершенного стандарта? :) P.S. если вы обнаружили ошибки, которые все еще присутствуют в текущих версиях, надеюсь, вы сообщили о них, они не будут исправлены, если никто не сообщит об этом. –

+0

@ JonathanВозвратитесь, может быть, я назвал слишком резким. Я имел в виду, что даже с небольшим хобби-проектом, как с моим, я столкнулся с ошибками (не пропускающими реализации, ошибки) в последних версиях g ++. Кроме того, поддержка кажется несколько «изменчивой», потому что я столкнулся с некоторыми регрессиями в g ++ 4.6 или 4.7. Это совершенно понятно, поскольку C++ 11 очень новый, а набор функций довольно продвинут. Конечно, я сообщаю большую часть материала, который я нахожу. См. Http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52744 http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53009 http://gcc.gnu.org/bugzilla /show_bug.cgi?id=53181 –

Смежные вопросы