실제로 "호출 가능하지"않은 템플릿 템플릿 매개 변수가있는 operator()
멤버 함수 템플릿을 감지하려고합니다. 또한 무의미합니다. 함수 템플릿에는 실제 이름이 있어야합니다. 당신의 예는 정말로 operator
것을 가리킨다. 하지만 어쨌든 문제를 해결해 보겠습니다.
제가 작업하고있는 라이브러리 솔루션 용 플러그를 CallableTraits (다시 진행중인 작업)이라고합니다.
사례는 CallableTraits에서 처리하지 않지만이 라이브러리는 비슷한 문제를 해결하기 위해 설명 할 기법을 사용합니다. 이 기술은 총 해킹이지만, 표준 준수, 그리고 다음 플랫폼에서 나를 위해 작동 :
- GCC 5.2 이후
- 연타 3.5 이상
- 의 Visual Studio 2015 업데이트 1 - 기본적으로
를 작동
참고 : Visual Studio 2015 업데이트 2는 부분 전문화 과정에서 std::index_sequence<I...>
을 잘못 추론하기 때문에 깨졌습니다. 버그 보고서를 제출했습니다. 설명은 here을 참조하십시오.
참고 : 표준 라이브러리 구현에 std::disjunction
이없는 경우 대신 here 샘플 구현을 사용할 수 있습니다.
나는이 기술을 템플릿 웜이라고 부릅니다. 깊고 어두운 우물에 침을 뱉어내는 것과 같은 메타 프로그래밍 방식입니다.
템플릿 웜이란 무엇입니까?
- 템플릿 웜은 모든 것과 모든 것으로 변환 가능한 클래스입니다.
- 템플릿 웜 피연산자가있는 연산자 식은 항상 다른 템플릿 웜으로 평가됩니다.
- 템플릿 웜은 평가되지 않은 컨텍스트에서만 사용할 수 있습니다. 다시 말해
decltype
이 최상위 표현식을 둘러싸는 경우에만 std::declval<T>()
처럼 사용할 수 있습니다.
템플릿 웜은 그것이 존재하지 않을 것으로 예상되는 곳으로 스스로 움직이고 찾을 수있는 첫 번째 구체적인 유형에 집착합니다. 비슷한 방식으로 실제 웜은 7 월 어느 오후에 콘크리트에 붙어 있습니다.
문제를 해결하기 위해 인수없이 시작한 다음 재귀 적으로 최대 10 개까지 작업합니다. 함수를 기준으로 템플릿 웜을 전달하려고 시도하여 (잠재적 인) 함수 객체를 호출하려고합니다. 스타일 호출, AND (템플릿 요구 사항 별).
이 코드는 의미가 더 많은 코드를 필요로하기 때문에 INVOKE 의미를 설명하지 않습니다. 멤버 함수에 대한 포인터와 멤버 함수에 대한 포인터로 작업해야 할 필요가있는 경우이를위한 자체 구현을 롤백 할 수 있습니다.
나는 모든 연산자를 다루지 않았을 수 있으며 올바르게 구현하지 않았지만 그 점을 확인할 수 있습니다.
마지막으로 한 가지 :
나는 하나의 캐치를 알고 있습니다. 반환 유형은 dependent name (멤버 연산자 제외)에 의존 할 수 없습니다.
편집 : 또한 호출/템플릿 인스턴스화는 SFINA에게 친숙해야합니다 (즉, static_assert
).
#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() {}
는이 유용한 것이 될 수있다 : (당신이 요청하지 않았 할 수도 있지만) 그 이상의 야단 법석없이
가 여기에 독립형 솔루션입니다 http://stackoverflow.com/questions/9117603/ how-does-has-has-has-member-class-template-work/9117836 # 9117836 – Lol4t0오버로드는 반환 유형을 기반으로하지 않습니다. –
@MatthieuM. 미안 내 실수. 나는 실수로 두 가지 다른 질문을 혼합했다. :(나는 질문을 편집했다. 나는 인수가없고 하나 이상의 템플릿 인자가있는 템플릿 연산자()의 존재를 감지 할 수있는 방법이 있는지 묻고 싶다. 단순히 operator()를 호출 할 수 없다. 얼마나 많은 템플릿 인자가 있는지에 대한 정보가 없기 때문에 그것을 인스턴스화하는 방법을 모른다. 즉, 템플릿 인자를 추론 할 방법이 없다. –