Check if a class has a possibly-overloaded function call operator - c++

I am wondering whether it would be possible to implement a trait in C++20 to check if a type T is such that it has a possibly overloaded/possibly templated function call operator: operator().
// Declaration
template <class T>
struct has_function_call_operator;
// Definition
???
// Variable template
template <class T>
inline constexpr bool has_function_call_operator_v
= has_function_call_operator<T>::value;
so that a code such as the following would lead to the correct result:
#include <iostream>
#include <type_traits>
struct no_function_call_operator {
};
struct one_function_call_operator {
constexpr void operator()(int) noexcept;
};
struct overloaded_function_call_operator {
constexpr void operator()(int) noexcept;
constexpr void operator()(double) noexcept;
constexpr void operator()(int, double) noexcept;
};
struct templated_function_call_operator {
template <class... Args>
constexpr void operator()(Args&&...) noexcept;
};
struct mixed_function_call_operator
: overloaded_function_call_operator
, templated_function_call_operator {
};
template <class T>
struct has_function_call_operator: std::false_type {};
template <class T>
requires std::is_member_function_pointer_v<decltype(&T::operator())>
struct has_function_call_operator<T>: std::true_type {};
template <class T>
inline constexpr bool has_function_call_operator_v
= has_function_call_operator<T>::value;
int main(int argc, char* argv[]) {
std::cout << has_function_call_operator_v<no_function_call_operator>;
std::cout << has_function_call_operator_v<one_function_call_operator>;
std::cout << has_function_call_operator_v<overloaded_function_call_operator>;
std::cout << has_function_call_operator_v<templated_function_call_operator>;
std::cout << has_function_call_operator_v<mixed_function_call_operator>;
std::cout << std::endl;
}
Currently it prints 01000 instead of 01111. If it is not doable in the broadest possible sense, it can be assumed that T is inheritable if that helps. The weirdest possible template metaprogramming tricks are welcome as long as they are fully compliant with the C++20 standard.

&T::operator() is ambiguous for the 3 failing cases.
So your traits found is there is an unambiguous operator()
As you allow T to be not final, we might apply your traits to (fake) class with existing inherited operator() and class to test:
template <class T>
struct has_one_function_call_operator: std::false_type {};
template <class T>
requires std::is_member_function_pointer_v<decltype(&T::operator())>
struct has_one_function_call_operator<T>: std::true_type {};
struct WithOp
{
void operator()() const;
};
template <typename T>
struct Mixin : T, WithOp {};
// if T has no `operator()`, Mixin<T> has unambiguous `operator()` coming from `WithOp`
// else Mixin<T> has ambiguous `operator()`
template <class T>
using has_function_call_operator =
std::bool_constant<!has_one_function_call_operator<Mixin<T>>::value>;
template <class T>
inline constexpr bool has_function_call_operator_v
= has_function_call_operator<T>::value;
Demo

Related

How to partially specialize NTTP class template for const and non-const member function pointer

Inspired by the section at the bottom of the page template <auto> here
I have been trying to figure out how to make it work when the function is part of an overload set for example 2 functions a const and a non-const. I tried several things the struct Bar and Bar2 are 2 that I think should work but I am doing something wrong.
Reference Code
class TestStruct {
public:
constexpr static int m_staticValue = 42;
uint8_t m_value = m_staticValue;
void somefunction(int& value)
{
std::cout << "somefunction " << value << "\n";
}
void somefunction(int& value) const
{
std::cout << "somefunction const " << value + 1 << "\n";
}
};
template <typename T>
struct Foo;
template <typename Ret, typename... Args>
struct Foo<Ret(Args...)> {
template <auto>
struct Bar: std::false_type
{};
template <typename Instance_T>
using MemberFunction_Ptr = void(std::remove_reference_t<Instance_T>::*)(int&);
template <typename Instance_T>
using MemberFunctionConst_Ptr = void(std::remove_reference_t<Instance_T>::*)(int&) const;
template <typename Instance_T>
using MemberFunctionConstOrNot_Ptr = std::conditional_t<std::is_const_v<std::remove_reference_t<Instance_T>>, MemberFunctionConst_Ptr<Instance_T>, MemberFunction_Ptr<Instance_T>>;
template <typename T, MemberFunctionConstOrNot_Ptr<T> MF>
struct Bar<MF> : std::true_type {
template <typename U>
Bar(U&& instance)
{
}
};
template <auto>
struct Bar2: std::false_type
{};
template <typename T, void (T::*MF)(int&)>
struct Bar2<MF> : std::true_type {
template <typename U>
Bar2(U&& instance)
{
}
};
template <typename T, void (T::*MF)(int&) const>
requires std::is_const_v<std::remove_reference_t<T>>
struct Bar2<MF> {
template <typename U>
Bar2(const U&& instance)
{
}
};
};
int main()
{
auto test = std::mem_fn<void(int&)>(&TestStruct::somefunction);
auto testConst = std::mem_fn<void(int&)const>(&TestStruct::somefunction);
int value = 42;
double valueDouble = 42;
TestStruct testStruct;
auto a = Foo<void(int&)>::Bar<&TestStruct::somefunction> { testStruct };
auto b = Foo<void(int&)>::Bar2<&TestStruct::somefunction> { testStruct };
return 0;
}
This auto a = Foo<void(int&)>::Bar<&TestStruct::somefunction> { testStruct }; gives this error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
and the second auto b = Foo<void(int&)>::Bar2<&TestStruct::somefunction> { testStruct };gives this error: non-type template parameter '' with type 'auto' has incompatible initializer of type ''
And I don't know how to fix any of those. I am maybe not approaching the problem from the right angle. But I feel like if we can specialize partially NTTP for member function pointer we should be able to have 2 specializations one for const and the other for non const. No?
Thank you.

C++ detection idiom failure with inheritance

The below code fails to compile. For some reason inheriting from HasFoo causes IsWrapper to fail. It has something to do with the friend function foo() because inheriting from other classes seems to work fine. I don't understand why inheriting from HasFoo causes the detection idiom to fail.
What is the proper way to detect WithFoo as a Wrapper?
https://godbolt.org/z/VPyarN
#include <type_traits>
#include <iostream>
template<typename TagType, typename ValueType>
struct Wrapper {
ValueType V;
};
// Define some useful metafunctions.
template<typename Tag, typename T>
T BaseTypeImpl(const Wrapper<Tag, T> &);
template<typename T>
using BaseType = decltype(BaseTypeImpl(std::declval<T>()));
template<typename Tag, typename T>
Tag TagTypeImpl(const Wrapper<Tag, T> &);
template<typename T>
using TagType = decltype(TagTypeImpl(std::declval<T>()));
// Define VoidT. Not needed with C++17.
template<typename... Args>
using VoidT = void;
// Define IsDetected.
template<template <typename...> class Trait, class Enabler, typename... Args>
struct IsDetectedImpl
: std::false_type {};
template<template <typename...> class Trait, typename... Args>
struct IsDetectedImpl<Trait, VoidT<Trait<Args...>>, Args...>
: std::true_type {};
template<template<typename...> class Trait, typename... Args>
using IsDetected = typename IsDetectedImpl<Trait, void, Args...>::type;
// Define IsWrapper true if the type derives from Wrapper.
template<typename T>
using IsWrapperImpl =
std::is_base_of<Wrapper<TagType<T>, BaseType<T>>, T>;
template<typename T>
using IsWrapper = IsDetected<IsWrapperImpl, T>;
// A mixin.
template<typename T>
struct HasFoo {
template<typename V,
typename std::enable_if<IsWrapper<T>::value &&
IsWrapper<V>::value>::type * = nullptr>
friend void foo(const T &This, const V &Other) {
std::cout << typeid(This).name() << " and " << typeid(Other).name()
<< " are wrappers\n";
}
};
template<typename Tag>
struct WithFoo : public Wrapper<WithFoo<Tag>, int>,
public HasFoo<WithFoo<Tag>> {};
int main(void) {
struct Tag {};
WithFoo<Tag> WrapperFooV;
// Fails. Why?
static_assert(IsWrapper<decltype(WrapperFooV)>::value,
"Not a wrapper");
return 0;
}
I don't understand why inheriting from HasFoo causes the detection idiom to fail.
Isn't completely clear to me also but surely a problem is that you use IsWrapper<T> inside the body of HasFoo<T> and, when you inherit HasFoo<WithFoo<Tag>> from WithFoo<Tag> you have that WithFoo<Tag> is incomplete when you check it with IsWrapper.
A possible solution (I don't know if acceptable for you) is define (and SFINAE enable/disable) foo() outside HasFoo.
I mean... try rewriting HasFoo as follows
template <typename T>
struct HasFoo {
template <typename V>
friend void foo(const T &This, const V &Other);
};
and defining foo() outside
template <typename T, typename V>
std::enable_if_t<IsWrapper<T>::value && IsWrapper<V>::value>
foo(const T &This, const V &Other) {
std::cout << typeid(This).name() << " and " << typeid(Other).name()
<< " are wrappers\n";
}
What is the proper way to detect WithFoo as a Wrapper?
Sorry but your code is too complicated for me.
I propose the following (simpler, I hope) alternative
#include <type_traits>
#include <iostream>
template<typename TagType, typename ValueType>
struct Wrapper {
ValueType V;
};
template <typename T1, typename T2>
constexpr std::true_type IW_helper1 (Wrapper<T1, T2> const &);
template <typename T>
constexpr auto IW_helper2 (T t, int) -> decltype( IW_helper1(t) );
template <typename T>
constexpr std::false_type IW_helper2 (T, long);
template <typename T>
using IsWrapper = decltype(IW_helper2(std::declval<T>(), 0));
template <typename T>
struct HasFoo {
template <typename V>
friend void foo(const T &This, const V &Other);
};
template <typename T, typename V>
std::enable_if_t<IsWrapper<T>::value && IsWrapper<V>::value>
foo(const T &This, const V &Other) {
std::cout << typeid(This).name() << " and " << typeid(Other).name()
<< " are wrappers\n";
}
template<typename Tag>
struct WithFoo : public Wrapper<WithFoo<Tag>, int>,
public HasFoo<WithFoo<Tag>> {};
int main () {
struct Tag {};
WithFoo<Tag> WrapperFooV;
static_assert(IsWrapper<decltype(WrapperFooV)>::value,
"Not a wrapper");
}

why C++ template type matching does not match to base class refrence, how can I make it match to base class refrence?

Here is my C++ program code
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <typename T>
void func(const A<T>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
}
and it prints 1. But what I expect is the function call prints 2. Why B<int>b matches to const T& instead of const A<T>&. And how can I make it to match to const A<T>&?
Solution 1
You could use std::enable_if and templated template parameters to get a better match for the function, like:
#include <iostream>
#include <memory>
#include <type_traits>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <template <typename...> typename T, typename ...Args, typename = std::enable_if_t<std::is_base_of_v<A<Args...>, T<Args...>>>>
void func(const T<Args...>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
func(5);
}
However, this works only if A takes exactly the same template parameters as T. So if your B changes to ex.
template <typename T, typename U>
struct B : A<T> {}
this won't work anymore.
Solution 2
Based on Yakk's answer you can create a type trait that is more generic. This solution has no restrictions to its template parameters, like solution 1 does.
namespace detail
{
template <template <typename...> typename Base>
struct template_base_detector
{
template <typename... Args>
constexpr std::true_type operator()(Base<Args...>*);
constexpr std::false_type operator()(...);
};
}
template <template <typename...> typename Base, typename T>
struct is_template_base_of
: decltype(std::declval<detail::template_base_detector<Base>>()((T*)nullptr)) {};
// since C++ 14
template <template <typename...> typename Base, typename T>
constexpr bool is_template_base_of_v = is_template_base_of<Base, T>::value;
Depending on your c++ version, you can use different approaches to utilize this trait.
C++ 17
Probably the most compact solution. Since C++ 17 constexpr if statements are allowed, which allows us to define just a single func:
template <typename T>
void func(const T& t)
{
if constexpr (is_template_base_of_v<A, T>)
cout << 2 << endl;
else
cout << 1 << endl;
}
C++ 11 and 14
We have to fall back to tag dispatch:
namespace detail
{
template <typename T>
void func(std::true_type, const T& t)
{
std::cout << 2 << endl;
}
template <typename T>
void func(std::false_type, const T& t)
{
std::cout << 1 << endl;
}
}
template <typename T>
void func(const T& t)
{
detail::func(is_template_base_of<A, T>{}, t);
}
Tag dispatching.
First, we write a trait to detect if something has a template as a base:
namespace details {
template<template<class...>class Z>
struct htb {
template<class...Ts>
constexpr std::true_type operator()(Z<Ts...>*){return {};}
constexpr std::false_type operator()(...){return {};}
};
}
template<template<class...>class Z, class X>
constexpr inline auto has_template_base = details::htb<Z>{}((X*)nullptr);
we can now use our new trait to tag dispatch:
namespace details{
template <typename T>
void func(std::true_type,const A<T>& t) {
std::cout<<"2"<<std::endl;
}
template <class T>
void func(std::false_type,const T& t) {
std::cout<<"1"<<std::endl;
}
}
template <typename T>
void func(const T& t) {
details::func(has_template_base<A,T>,t);
}
Live example.

Test if calling f(x) is possible using metaprogramming

The Stroustrup's book provides an example how to answer the question: "is it possible to call f(x) if x is of type X" (the section 28.4.4 "Further examples with Enable_if"). I've tried to reproduce the example but got something wrong and can't understand what.
In my code below, there is a function f(int). I expect that then the result of has_f<int>::value is 1 (true). The actual result is 0 (false).
#include <type_traits>
#include <iostream>
//
// Meta if/then/else specialization
//
struct substitution_failure { };
template<typename T>
struct substitution_succeeded : std::true_type { };
template<>
struct substitution_succeeded<substitution_failure> : std::false_type { };
//
// sfinae to derive the specialization
//
template<typename T>
struct get_f_result {
private:
template<typename X>
static auto check(X const& x) -> decltype(f(x));
static substitution_failure check(...);
public:
using type = decltype(check(std::declval<T>()));
};
//
// has_f uses the derived specialization
//
template<typename T>
struct has_f : substitution_succeeded<typename get_f_result<T>::type> { };
//
// We will check if this function call be called,
// once with "char*" and once with "int".
//
int f(int i) {
std::cout << i;
return i;
}
int main() {
auto b1{has_f<char*>::value};
std::cout << "test(char*) gives: " << b1 << std::endl;
std::cout << "Just to make sure we can call f(int): ";
f(777);
std::cout << std::endl;
auto b2{has_f<int>::value};
std::cout << "test(int) gives: " << b2 << std::endl;
}
The output:
test(char*) gives: 0
Just to make sure we can call f(int): 777
test(int) gives: 0
The main problem is that you're making an unqualified call to f here:
template<typename X>
static auto check(X const& x) -> decltype(f(x));
The fs that will be found will be those in scope at the point of definition of check() (none) and those that are found by argument-dependent lookup in the associated namespaces of X. Since X is int, it has no associated namespaces, and you find no fs there either. Since ADL will never work for int, your function has to be visible before get_f_result is defined. Just moving it up solves that problem.
Now, your has_f is overly complicated. There is no reason for the substitution_succeeded machinery. Just have the two check() overloads return the type you want:
template<typename T>
struct has_f {
private:
template <typename X>
static auto check(X const& x)
-> decltype(f(x), std::true_type{});
static std::false_type check(...);
public:
using type = decltype(check(std::declval<T>()));
};
And now has_f<T>::type is already either true_type or false_type.
Of course, even this is overly complicated. Checking if an expression is valid is a fairly common operation, so it'd be helpful to simplify it (borrowed from Yakk, similar to std::is_detected):
namespace impl {
template <template <class...> class, class, class... >
struct can_apply : std::false_type { };
template <template <class...> class Z, class... Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type { };
};
template <template <class... > class Z, class... Ts>
using can_apply = impl::can_apply<Z, void, Ts...>;
This let's you write:
template <class T>
using result_of_f = decltype(f(std::declval<T>()));
template <class T>
using has_f = can_apply<result_of_f, T>;
I can see 2 ways to fix the issue you are seeing:
Forward declare your function f. This is required because you are explicitly calling the function by name in the template get_f_result.
int f(int);
template<typename T>
struct get_f_result {
private:
template<typename X>
static auto check(X const& x) -> decltype(f(x));
static substitution_failure check(...);
public:
using type = decltype(check(std::declval<T>()));
};
The second solution is to make it more generic i.e not just for f(c) but for all function which takes an int:
//
// sfinae to derive the specialization
//
template <typename Func, Func f, typename T>
struct get_f_result {
private:
template <typename X>
static auto check(X const& x) -> decltype(f(x));
static substitution_failure check(...);
public:
using type = decltype(check(std::declval<T>()));
};
And Call it like:
template <typename T>
struct has_f : substitution_succeeded <typename get_f_result::type> { };
Again here f needs to be known here..but, you can again make it more generic by shifting the responsibility of providing the function at the caller site.

Template function specialization for template class

Is it possible to write something like this in C++11/14?
#include <iostream>
#include <vector>
template <typename T>
T Get();
template <typename T>
struct Data {
std::vector<T> data;
};
template <>
template <typename T>
Data<T> Get<Data<T>>() {
return Data<T>{{T{}, T{}}};
}
template <>
template <typename T>
std::vector<T> Get<std::vector<T>>() {
return std::vector<T>(3);
}
int main() {
std::cout << Get<Data<int>>().data.size() << std::endl; // expected output is 2
std::cout << Get<std::vector<int>>().size() << std::endl; // expected output is 3
return 0;
}
Overloading won't help in this case, since call to Get<...>() will be ambiguious (see):
template <typename T>
Data<T> Get() {
return Data<T>{{T{}, T{}}};
}
template <typename T>
std::vector<T> Get() {
return std::vector<T>(3);
}
Any direction on how to overcome this are welcome.
There is workaround, that gives you something like this: do not specialize - overload:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
template <typename T>
size_t Get(const T& data)
{
return 444;
}
template <typename T>
struct Data
{
std::vector<T> data;
};
template <typename T>
size_t Get(const Data<T>& data) {
return data.data.size();
}
int main() {
std::cout << Get<>(0) << std::endl; // expected output is 444
std::cout << Get<>(Data<int>{}) << std::endl; // expected output is 0
return 0;
}
Output:
444
0
Note, that size_t Get(const Data<T>& data) is not a specialization - it is completely "different" Get(), that is called for argument of type Data<T> for any T.
Here you can see working sample.
EDIT
I see you changed your question completely. However, I will still try to answer it. There is a standard workaround for lack of partial function specialization - using delegation to structs/classes.
Here is what you need:
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
struct GetImpl;
template <typename T>
struct Data {
std::vector<T> data;
};
template <typename T>
struct GetImpl< Data<T> >
{
static Data<T> Get() {
return Data<T>{ {T{}, T{}} };
};
};
template <typename T>
struct GetImpl< std::vector<T> >
{
static std::vector<T> Get() {
return std::vector<T>(3);
};
};
int main() {
std::cout << GetImpl< Data<int> >::Get().data.size() << std::endl; // expected output is 2
std::cout << GetImpl< std::vector<int> >::Get().size() << std::endl; // expected output is 3
return 0;
}
Output:
2
3
Working sample can be found here.
If you don't like the syntax, you can make it a little bit shorter, by changing static function Get() to function call operator:
template <typename T>
struct Get< Data<T> >
{
Data<T> operator()() {
return Data<T>{ {T{}, T{}} };
};
};
template <typename T>
struct Get< std::vector<T> >
{
std::vector<T> operator()() {
return std::vector<T>(3);
};
};
And then:
Get< Data<int> >()().data.size();
Get< std::vector<int> >()().size();
You have only two extra characters - (). This is the shortest solution I can think of.
As Columbo mentioned in his comment, you should apply the standard workaround for lack of partial specialization support for functions: delegation to a partially specialized class:
template <typename T>
struct GetImpl;
template <typename T>
T Get() { return GetImpl<T>::Do(); }
and now use partial specialization on struct GetImpl<T> { static T Do(); } instead of Get<T>()
But it would be impossible for compiler to distinguish Get<Data<int>> from Get<Data<Data<int>>>.
It's not impossible. If that's something you need to do, we can add separate overloads:
template <typename T>
size_t Get(const Data<T>& data);
template <typename T>
size_t Get(const Data<Data<T>>& data); // preferred for Data<Data<int>>
Or if what you want is to only overload for the non-nested case, we can add a type trait and use SFINAE:
template <typename T> struct is_data : std::false_type { };
template <typename T> struct is_data<Data<T>> : std::true_type { };
template <typename T>
enable_if_t<!is_data<T>::value, size_t>
Get(const Data<T>& data);
That way, the call with Data<Data<int>> would call the generic Get(const T&). Or, if you want that case to not compile at all:
template <typename T>
size_t Get(const Data<T>& data) {
static_assert(!is_data<T>::value, "disallowed");
...
}
So overloading gives you lots of options. Specialization gives you none, since it's disallowed anyway.
Following delegation to the struct's way you can implement more general approach: you can use structs to check the container type and inner type like this:
#include <iostream>
#include <vector>
template <typename T>
struct Data {
std::vector<T> data;
};
template <template <typename...> class Container, typename>
struct get_inner;
template <template <typename...> class Container, typename T>
struct get_inner<Container, Container<T>>
{
typedef T type;
};
template <typename T, typename U = typename get_inner<Data, T>::type>
Data<U> Get() {
return Data<U>{ {U{}, U{}} };
}
template <typename T, typename U = typename get_inner<std::vector, T>::type>
std::vector<U> Get() {
return std::vector<U>(3);
}
int main() {
std::cout << Get<Data<int>>().data.size() << std::endl; // expected output is 2
std::cout << Get<std::vector<int>>().size() << std::endl; // expected output is 3
return 0;
}
http://coliru.stacked-crooked.com/a/90b55767911eff0e