3

Cela est apparu lorsque je recherchais un bogue dans le wrapper boost::fusion::fused lors de l'utilisation de decltype. Le problème semble être qu'un decltype invalide est une erreur de compilation, même si l'instanciation de modèle qui le requiert ne sera pas utilisée, et je ne peux pas comprendre comment contourner cela pour créer un wrapper de fonction générique.Des encapsuleurs de fonctions génériques en C++ sont-ils possibles?

Voilà ma tentative de l'emballage unique argument:

#include <utility> 
#include <type_traits> 

template <class T> 
typename std::add_rvalue_reference<T>::type declval(); 

template <class Fn, class Arg> 
struct get_return_type 
{ 
    typedef decltype(declval<Fn>()(declval<Arg>())) type; 
}; 

template <class Fn> 
struct wrapper 
{ 
    explicit wrapper(Fn fn) : fn(fn) {} 
    Fn fn; 

    template <class Arg> 
    typename get_return_type<Fn,Arg&&>::type 
     operator()(Arg&& arg) 
    { 
     return fn(std::forward<Arg>(arg)); 
    } 

    template <class Arg> 
    typename get_return_type<const Fn,Arg&&>::type 
     operator()(Arg&& arg) 
    { 
     return fn(std::forward<Arg>(arg)); 
    } 
}; 

Le problème est, cela ne fonctionne pas pour les cas où les arguments à la version non-const ne sont pas convertibles aux arguments de la const version. Par exemple:

#include <iostream> 

struct x {}; 
struct y {}; 

struct foo 
{ 
    void operator()(x) { std::cout << "void operator()(x)" << std::endl; } 
    void operator()(y) const { std::cout << "void operator()(y) const" << std::endl; } 
}; 

int main() 
{ 
    wrapper<foo> b = wrapper<foo>(foo()); 
    b(x()); // fail 
} 

Il me semble que l'échec de l'expression decltype causée par void operator()(y) const doit simplement donner lieu à cette fonction étant retirée en raison de SFINAE.

+0

ne devrait pas être là un retour dans vos opérateurs pour l'emballage? –

+0

@VJovic Oups! Merci, je les ai ajoutés maintenant. – Ayjay

+0

Le deuxième opérateur() dans l'encapsuleur doit également être const. Quel compilateur? Quelle est l'erreur? Pour g ++ 4.6.1, je reçois une erreur bizarre: 'no match for call to (const foo) (x)' –

Répondre

0

Voici le code qui compile bien sur g ++ 4.6.1:

#include <utility> 
#include <type_traits> 
#include <iostream> 

template <class Fn, class Arg> 
struct get_return_type 
{ 
    typedef decltype(std::declval<Fn>() (std::declval<Arg>())) type; 
}; 

template <class Fn> 
struct wrapper 
{ 
    explicit wrapper(Fn fn) : fn(fn) {} 
    Fn fn; 

    template <class Arg> 
    typename get_return_type<Fn,Arg&&>::type 
    operator()(Arg&& arg) 
    { 
     return fn(std::forward<Arg>(arg)); 
    } 

    template <class Arg> 
    typename get_return_type< const Fn,Arg&&>::type 
    operator()(Arg&& arg) const 
    { 
     return fn(std::forward<Arg>(arg)); 
    } 
}; 

struct x {}; 
struct y {}; 

struct foo 
{ 
    x* operator()(x) { std::cout << "x* operator()(x)" << std::endl; return nullptr; } 
    x operator()(x) const { std::cout << "x operator()(x) const" << std::endl; return x(); } 
    y* operator()(y) { std::cout << "y* operator()(y)" << std::endl; return nullptr; } 
    y operator()(y) const { std::cout << "y operator()(y) const" << std::endl; return y(); } 
}; 

template <class Fn> 
void test_foo(Fn fn) 
{ 
    // make sure all operator() overloads are callable 
    const Fn& cfn = fn; 

    x* a = fn(x()); 
    x b = cfn(x()); 
    y* c = fn(y()); 
    y d = cfn(y()); 
(void)a;(void)b;(void)c;(void)d; 
} 

int main() 
{ 
    test_foo(foo()); 
    test_foo(wrapper<foo>(foo())); // fail 
} 
+0

Eh bien, si vous changez le foncteur, alors bien sûr, mais le point est peut-on écrire un wrapper de fonction qui peut envelopper chaque fonction? – Ayjay

+0

@Ayjay Le code ci-dessus est une version corrigée qui compile bien pour x et y :) –

+0

Mais maintenant, il ne détermine pas correctement le type de retour d'un foncteur 'const'. – Ayjay

Questions connexes