2012-09-03 12 views
6

Estoy tratando de crear una función de plantilla que acepte un iterable y una función, de modo que la función pasada se convertirá implícitamente en std::function del tipo apropiado (lo que permite su uso tanto con funciones completas como con lambdas).(g ++ 4.7.1) Reemplazar el nombre de tipo explícito con una clase equivalente typedef no puede compilar

Aquí está el código:

#include <iostream> 
#include <vector> 
#include <algorithm> 
#include <typeinfo> 


template<typename T> 
void bar(const T & base, std::function<bool(int)> f) // works 
//void bar(const T & base, std::function<bool(typename T::iterator::value_type)> f) // fails to compile 
{ 
    std::cout << ((typeid(std::function<bool(int)>) == typeid(std::function<bool(typename T::iterator::value_type)>))?"identical":"distinct") << std::endl; 
} 

bool filter(int x) { return x%2==0; } 

int main() { bar(std::vector<int> {0, 1}, filter); } 

compilado con g++-4.7 -std=c++11 -o itest itest.cpp esto produce identical.

Si la línea 10 y la línea de comentario uncomment 9 y compilar que el anterior, en vez de compilación falla con

g++-4.7 -std=c++11 -Wall -Werror -o itest itest.cpp 
itest.cpp: In function 'int main()': 
itest.cpp:16:53: error: no matching function for call to 'bar(std::vector<int>, bool (&)(int))' 
itest.cpp:16:53: note: candidate is: 
itest.cpp:9:10: note: template<class T> void bar(const T&, std::function<bool(typename T::iterator::value_type)>) 
itest.cpp:9:10: note: template argument deduction/substitution failed: 
itest.cpp:16:53: note: mismatched types 'std::function<bool(typename T::iterator::value_type)>' and 'bool (*)(int)' 

Debo señalar que la versión no modificada tiene éxito con Xcode (habiendo establecido las opciones apropiadas), pero me prefiero seguir con g ++ sobre clang si es posible. ¿Estoy haciendo algo mal, o se trata de un error conocido en g ++?

Respuesta

5

Lo sentimos, pero el error está en su código. Es equivalente a:

template<typename T> struct S { template<typename U> S(const U &); }; 
template<typename T> void bar(T, S<T>); 
int main() { bar(5, 6); } 

La cuestión es que en el argumento de plantilla deducción/sustitución, si aparece un argumento de plantilla (ya sea directamente o en la construcción de un tipo dependiente) en más de un argumento a continuación, ambos argumentos tienen que coincidir exactamente; las conversiones definidas por el usuario no se consideran, incluso si es obvio a partir de un argumento cuál debe ser el tipo.

La conversión definida por el usuario aquí es el constructor implícito de std::function<...>.

solución posible sería crear una instancia de bar explícitamente (como bar<int>), o para despachar a una función auxiliar:

template<typename T> 
void bar_impl(const T & base, std::function<bool(typename T::iterator::value_type)> f) 
{ 
    std::cout << ((typeid(std::function<bool(int)>) == typeid(std::function<bool(typename T::iterator::value_type)>))?"identical":"distinct") << std::endl; 
} 

template<typename T, typename F> 
void bar(const T & base, F &&f) 
{ 
    bar_impl<T>(base, std::forward<F>(f)); 
} 
+0

Huh, gracias. Implementé otra (bastante terrible) solución, una que no requiere que la persona que llama se refiera ni obtenga el segundo tipo. – Bakkot

+0

Vaya, olvidé el [enlace] (https://gist.github.com/a172651b93dc4670f295). Para las personas que no quieren leerlo, es básicamente currying. – Bakkot

2

Es necesario segunda sobrecarga para la función de puntero - a continuación, se compilará. conversión implícita a std::function no funcionará:

void bar(const T & base, bool(*f)(typename T::value_type)){ 
    std::cout << "ptr func\n"; 
} 

trabajo en torno a un problema descrito por ecatmur (varios T, con el que no coincida con los tipos en signutre función): se puede envolver otra T en identity estructura, que se define así:

template<class T> struct identity{ typedef T type; }; 

entonces el compilador ignorará estas T para la deducción de tipo.

Cuestiones relacionadas