Está intentando detectar una plantilla de función de miembro operator()
con parámetros de plantilla no deducidos, que en realidad no es "invocable" y también es inútil, la plantilla de función debería tener un nombre real, porque su ejemplo realmente pierde el sentido de todo el asunto operator
. Pero solucionemos tu problema de todos modos.
Permítanme prefacio con un enchufe para una solución de biblioteca en la que estoy trabajando, llamado CallableTraits (una vez más, un trabajo en progreso).
Aunque su caso no es manejado por CallableTraits, la biblioteca emplea una técnica que estoy a punto de describir para resolver un problema muy similar. La técnica es un corte total, pero que es compatible con el estándar, y trabaja para mí en las siguientes plataformas:
- GCC 5.2 y posterior
- Clang 3.5 y posterior
- Visual Studio 2015 Actualización 1 - básicamente funciona
Nota: Visual Studio 2015 Actualización 2 está roto, porque deduce incorrectamente std::index_sequence<I...>
en especializaciones parciales ... Archivé un informe de error. Ver here para una descripción.
Nota: Si la implementación de su biblioteca estándar no tiene std::disjunction
, puede utilizar la implementación de ejemplo here.
Llamo a la técnica plantilla gusano. Es el equivalente de la metaprogramación de escupir en un pozo profundo y oscuro, solo para escuchar cuánto tiempo lleva salpicar.
¿Qué es un gusano de plantilla?
- Una plantilla de gusano es una clase que es convertible a cualquier cosa.
- Cualquier expresión de operador con un operador de gusano de plantilla siempre evaluará a otro gusano de plantilla.
- Un gusano de plantilla solo se puede usar en un contexto no evaluado. En otras palabras, solo puede usarlo cuando
decltype
rodea la expresión de nivel superior, al igual que std::declval<T>()
.
Un gusano de plantilla se menea en lugares en los que no debería estar, y se adhiere al primer tipo de concreto que puede encontrar. De manera similar, un gusano real se pegará al concreto en cualquier tarde de julio.
Para resolver su problema, comenzaremos sin argumentos, luego trabajaremos recursivamente hasta un límite arbitrario de 10. Intentamos realizar la llamada al objeto de función (potencial) tratando de pasar la plantilla por función invocación de estilo, Y por argumento de tipo de plantilla (según sus requisitos).
Este código no tiene en cuenta la semántica de INVOKE, porque eso requiere significativamente más código. Si necesita que esto funcione con las funciones de punteros a miembros y punteros a datos de miembros, puede implementar su propia implementación para eso.
Puede que no haya cubierto todos los operadores, y puede que no los haya implementado todos correctamente, pero verá el punto.
Una última cosa:
Sé de una captura. El tipo de devolución no puede depender de dependent name (que no sean operadores miembros).
Editar: Además, la instanciación de invocación/plantilla debe ser compatible con SFINAE (es decir, no static_assert
s).
Sin más preámbulos, aquí es su solución independiente (aunque es posible que desearías no haber pedido):
#include <utility>
#include <type_traits>
namespace detail {
//template_worm CANNOT be used in evaluated contexts
struct template_worm {
template<typename T>
operator T&() const;
template<typename T>
operator T &&() const;
template_worm() = default;
#ifndef _MSC_VER
// MSVC doesn't like this... because it can deduce void?
// Whatever, we can do without it on Windows
template<typename... T>
template_worm(T&&...);
#endif //_MSC_VER
template_worm operator+() const;
template_worm operator-() const;
template_worm operator*() const;
template_worm operator&() const;
template_worm operator!() const;
template_worm operator~() const;
template_worm operator()(...) const;
};
#define TEMPLATE_WORM_BINARY_OPERATOR(...) \
\
template<typename T> \
constexpr inline auto \
__VA_ARGS__ (template_worm, T&&) -> template_worm { \
return template_worm{}; \
} \
\
template<typename T> \
constexpr inline auto \
__VA_ARGS__ (T&&, template_worm) -> template_worm { \
return template_worm{}; \
} \
\
constexpr inline auto \
__VA_ARGS__ (template_worm, template_worm) -> template_worm { \
return template_worm{}; \
} \
/**/
TEMPLATE_WORM_BINARY_OPERATOR(operator+)
TEMPLATE_WORM_BINARY_OPERATOR(operator-)
TEMPLATE_WORM_BINARY_OPERATOR(operator/)
TEMPLATE_WORM_BINARY_OPERATOR(operator*)
TEMPLATE_WORM_BINARY_OPERATOR(operator==)
TEMPLATE_WORM_BINARY_OPERATOR(operator!=)
TEMPLATE_WORM_BINARY_OPERATOR(operator&&)
TEMPLATE_WORM_BINARY_OPERATOR(operator||)
TEMPLATE_WORM_BINARY_OPERATOR(operator|)
TEMPLATE_WORM_BINARY_OPERATOR(operator&)
TEMPLATE_WORM_BINARY_OPERATOR(operator%)
TEMPLATE_WORM_BINARY_OPERATOR(operator,)
TEMPLATE_WORM_BINARY_OPERATOR(operator<<)
TEMPLATE_WORM_BINARY_OPERATOR(operator>>)
TEMPLATE_WORM_BINARY_OPERATOR(operator<)
TEMPLATE_WORM_BINARY_OPERATOR(operator>)
template<std::size_t Ignored>
using worm_arg = template_worm const &;
template<typename T>
struct success {};
struct substitution_failure {};
template<typename F, typename... Args>
struct invoke_test {
template<typename T, typename... Rgs>
auto operator()(T&& t, Rgs&&... rgs) const ->
success<decltype(std::declval<T&&>()(std::forward<Rgs>(rgs)...))>;
auto operator()(...) const->substitution_failure;
static constexpr int arg_count = sizeof...(Args);
};
// force_template_test doesn't exist in my library
// solution - it exists to please OP
template<typename... Args>
struct force_template_test {
template<typename T>
auto operator()(T&& t) const ->
success<decltype(std::declval<T&&>().template operator()<Args...>())>;
auto operator()(...) const->substitution_failure;
};
template<typename T, typename... Args>
struct try_invoke {
using test_1 = invoke_test<T, Args...>;
using invoke_result = decltype(test_1{}(
::std::declval<T>(),
::std::declval<Args>()...
));
using test_2 = force_template_test<Args...>;
using force_template_result = decltype(test_2{}(std::declval<T>()));
static constexpr bool value =
!std::is_same<invoke_result, substitution_failure>::value
|| !std::is_same<force_template_result, substitution_failure>::value;
static constexpr int arg_count = test_1::arg_count;
};
template<typename T>
struct try_invoke<T, void> {
using test = invoke_test<T>;
using result = decltype(test{}(::std::declval<T>()));
static constexpr bool value = !std::is_same<result, substitution_failure>::value;
static constexpr int arg_count = test::arg_count;
};
template<typename U, std::size_t Max, typename = int>
struct min_args;
struct sentinel {};
template<typename U, std::size_t Max>
struct min_args<U, Max, sentinel> {
static constexpr bool value = true;
static constexpr int arg_count = -1;
};
template<typename U, std::size_t Max, std::size_t... I>
struct min_args<U, Max, std::index_sequence<I...>> {
using next = typename std::conditional<
sizeof...(I)+1 <= Max,
std::make_index_sequence<sizeof...(I)+1>,
sentinel
>::type;
using result_type = std::disjunction<
try_invoke<U, worm_arg<I>...>,
min_args<U, Max, next>
>;
static constexpr bool value = result_type::value;
static constexpr int arg_count = result_type::arg_count;
};
template<typename U, std::size_t Max>
struct min_args<U, Max, void> {
using result_type = std::disjunction<
try_invoke<U, void>,
min_args<U, Max, std::make_index_sequence<1>>
>;
static constexpr int arg_count = result_type::arg_count;
static constexpr bool value = result_type::value;
};
template<typename T, std::size_t SearchLimit>
using min_arity = std::integral_constant<int,
min_args<T, SearchLimit, void>::arg_count>;
}
// Here you go.
template<typename T>
using is_callable = std::integral_constant<bool,
detail::min_arity<T, 10>::value >= 0>;
// This matches OP's first example.
struct Test1 {
template<typename T>
T operator()() {
return{};
}
};
// Yup, it's "callable", at least by OP's definition...
static_assert(is_callable<Test1>::value, "");
// This matches OP's second example.
struct Test2 {
template<typename T, typename U>
auto operator()() -> decltype(std::declval<T>() + std::declval<U>()) {
return{};
}
};
// Yup, it's "callable", at least by OP's definition...
static_assert(is_callable<Test2>::value, "");
// ints aren't callable, of course
static_assert(!is_callable<int>::value, "");
int main() {}
puede ser esto sería muy útil: http://stackoverflow.com/questions/9117603/ how-does-this-has-member-class-template-work/9117836 # 9117836 – Lol4t0
La sobrecarga nunca se basa en el tipo de devolución. –
@MatthieuM. Disculpa, me equivoque. Accidentalmente mezclé dos preguntas diferentes ... :(He editado la pregunta. Quería preguntar si hay alguna forma de detectar la presencia del operador con plantilla() sin argumentos y con uno o más argumentos de plantilla. No puedo simplemente llamar al operador() porque no hay información de cuántos argumentos de plantilla hay y no sé cómo crear una instancia, es decir, no hay forma de deducir los argumentos de la plantilla. –