std::is_member_function_pointer not working for overloaded functions? [duplicate] - c++

This question already has answers here:
Checking for existence of an (overloaded) member function
(3 answers)
Closed 2 days ago.
I've written a trait that detects whether a class has a certain public member function via std::is_member_function_pointer:
class Test1
{
public:
void method();
};
class Test2
{
public:
void method();
void method(int);
};
template <typename T, typename = void>
struct HasMethod : std::false_type
{
};
template <typename T>
struct HasMethod<T, std::enable_if_t<std::is_member_function_pointer<decltype(&T::method)>::value>>
: std::true_type
{
};
int main()
{
std::cout << HasMethod<Test1>::value << std::endl; // prints 1
std::cout << HasMethod<Test2>::value << std::endl; // prints 0
}
However it seems that when the function is overloaded, then std::is_member_function_pointer is not able to detect it, as shown with Test2.
Is there a way to make it work regardless of whether the method is overloaded?

To detect existence of class members usual approach is to use std::void_t template. You can do it like this:
#include <iostream>
#include <type_traits>
class Test1
{
public:
void method();
};
class Test2
{
public:
void method();
void method(int);
};
template <typename T, typename = void>
struct HasMethod : std::false_type
{
};
// Use method(0) to detect void method(int) overload
// You don't even need std::void_t in this case, since method returns void anyway, but for any other return type you will need it
template <typename T>
struct HasMethod<T, std::void_t<decltype(std::declval<T>().method())>>
: std::true_type
{
};
int main()
{
std::cout << HasMethod<Test1>::value << std::endl; // prints 1
std::cout << HasMethod<Test2>::value << std::endl; // prints 1
}
Ok. After some thinking I found solution. We can use fact that declval(&T::method) will fail, if there is more than one overload, to detect if we have at least one overload, by adding another one. Here is solution. It is quite verbose, but I was unable to reduce it. At least it works.
#include <iostream>
#include <type_traits>
class Test
{
};
class Test1
{
public:
void method();
};
class Test2
{
public:
void method();
void method(int);
};
class Test3
{
public:
using method = int;
};
class Test4
{
public:
int method;
};
template<typename T, typename = void>
struct HasSingleOverload : std::false_type {};
template<typename T>
struct HasSingleOverload<T, std::void_t<decltype(&T::method)>> : std::true_type {};
template<typename T, typename = void>
struct IsMemberFunction : std::false_type {};
template<typename T>
struct IsMemberFunction<T, std::enable_if_t<std::is_member_function_pointer<decltype(&T::method)>::value>> : std::true_type {};
template<typename T, typename = void>
struct IsType : std::false_type {};
template<typename T>
struct IsType<T, std::void_t<typename T::method>> : std::true_type {};
struct HasOverload {
void method();
};
template<typename T>
struct CheckOverload : T, HasOverload {
};
template<typename T>
using HasConflict = std::bool_constant<!HasSingleOverload<CheckOverload<T>>::value>;
template<typename T>
using HasAnyOverload = std::conditional_t<HasSingleOverload<T>::value || IsType<T>::value, IsMemberFunction<T>, HasConflict<T>>;
int main()
{
std::cout << HasAnyOverload<Test>::value << std::endl; // prints 0
std::cout << HasAnyOverload<Test1>::value << std::endl; // prints 1
std::cout << HasAnyOverload<Test2>::value << std::endl; // prints 1
std::cout << HasAnyOverload<Test3>::value << std::endl; // prints 0
std::cout << HasAnyOverload<Test4>::value << std::endl; // prints 0
}

Related

type trait to detect structs with specific methods which have const variants

I require a trait to detect when a struct has particular methods.
For example, I would like to detect when a struct has the two methods void foo() and void bar(SomeData), and false otherwise.
My attempt below works, except when the struct additionally has const variants of the required methods. I would like it to work in this case too as the requirement is only that the two non-const variants are present.
i.e. in the snippet below, has_methods<T> is true for TypeA, but false for TypeB.
How can I adapt this code to work for both TypeA and TypeB?
template<typename T, typename = void, typename = void>
struct has_methods : std::false_type {};
template<typename T>
struct has_methods<T,
std::void_t<decltype(&T::foo)>,
std::void_t<decltype(&T::bar)>> : std::true_type {};
struct SomeData {};
struct TypeA
{
void foo();
void bar(SomeData);
};
struct TypeB
{
void foo();
void foo() const;
void bar(SomeData);
void bar(SomeData) const;
};
int main()
{
std::cout << "has_methods<TypeA>: " << has_methods<TypeA>::value << std::endl;
std::cout << "has_methods<TypeB>: " << has_methods<TypeB>::value << std::endl;
return 0;
}
Desired output is:
has_methods<TypeA>: 1
has_methods<TypeB>: 1
Note that your has_methods isn't actually testing for methods. &T::foo matches any member named foo. As mentioned in a comment you get a false positive for struct C { int foo; std::string bar; };. Fortunately the answer to your question also fixes that.
You can use a static_cast to select the desired overload like this:
template<typename T>
struct has_methods<T,
std::void_t<decltype(static_cast<void (T::*)()>(&T::foo))>,
std::void_t<decltype(static_cast<void (T::*)(SomeData)>(&T::bar))>> : std::true_type {};
Note that a declaration of SomeData has to be available (you could make that a parameter of has_methods, but for the sake of simplicity I just rearranged the code to define SomeData first). Complete example:
#include <type_traits>
#include <iostream>
struct SomeData {};
template<typename T, typename = void, typename = void>
struct has_methods : std::false_type {};
template<typename T>
struct has_methods<T,
std::void_t<decltype(static_cast<void (T::*)()>(&T::foo))>,
std::void_t<decltype(static_cast<void (T::*)(SomeData)>(&T::bar))>> : std::true_type {};
struct TypeA
{
void foo();
void bar(SomeData);
};
struct TypeB
{
void foo();
void foo() const;
void bar(SomeData);
void bar(SomeData) const;
};
int main()
{
std::cout << "has_methods<TypeA>: " << has_methods<TypeA>::value << std::endl;
std::cout << "has_methods<TypeB>: " << has_methods<TypeB>::value << std::endl;
return 0;
}
Output:
has_methods<TypeA>: 1
has_methods<TypeB>: 1
(I'd add some negatives to have a more convincing test, but I didn't want to change more than necessary on your code, and partly lazyness ;)

Unable to discriminate template specialization with enable_if and is_base_of

I am trying to trade some run-time checks for compile-time checks to identify the base class of an object with template specializations.
The code compiles fine, but I can't figure out why the enable_if statement always end up invalid or equal to void because I'm always landing on the base template struct.
#include <iostream>
#include <type_traits>
using namespace std;
struct BaseOne {};
struct DerivedOne : BaseOne {};
struct BaseTwo {};
struct DerivedTwo : BaseTwo {};
struct Default {};
template<typename T, typename = void>
struct get_category_impl {
static constexpr int value = 0;
};
template<typename T>
struct get_category_impl<T, typename enable_if<is_base_of<BaseOne, T>::value, T>::type> {
static constexpr int value = 1;
};
template<typename T>
struct get_category_impl<T, typename enable_if<is_base_of<BaseTwo, T>::value, T>::type> {
static constexpr int value = 2;
};
template<typename T>
constexpr int get_category = get_category_impl<T>::value;
int main() {
cout << get_category<BaseOne> << "\n"; // prints 0
cout << get_category<DerivedOne> << "\n"; // prints 0
cout << get_category<BaseTwo> << "\n"; // prints 0
cout << get_category<DerivedTwo> << "\n"; // prints 0
cout << get_category<Default> << "\n"; // prints 0
}
The second parameter to enable_if doesn't need to be specified. If you do specify it, it needs to somehow resolve to void. Since you've specified the second parameter as T, this doesn't work.
Instead, just do this:
template<typename T>
struct get_category_impl<T, typename enable_if<is_base_of<BaseOne, T>::value>::type> {
// ^ No T
static constexpr int value = 1;
};
and similarly for the other specialization.
Here's a demo.

Template member function specialization in a template class

I have a template class and a member function print() to print the data.
template<typename T>
class A
{
public:
T data;
void print(void)
{
std::cout << data << std::endl;
}
// other functions ...
};
Then, I want to either print scalar data or vector data, so I give a specialized definition and get a compiler error.
template<typename T>
void A<std::vector<T>>::print(void) // template argument list error
{
for (const auto& d : data)
{
std::cout << d << std::endl;
}
}
Question: Why does this member function specialization get an error? What is the correct way to define a print function for a vector?
Solution 1: I have tested the following definition.
template<typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print(void) { // OK
// ...
}
}
This one worked, but I have to copy the other member functions into this specialized class.
EDIT:
Solution 2: To prevent copy all the other member functions, I define a base class containing the common member functions and inherit from the base class:
template<typename T>
class Base
{
public:
T data;
// other functions ...
};
template<typename T>
class A : public Base<T>
{
public:
void print(void)
{
std::cout << this->data << std::endl;
}
};
template<typename T>
class A<std::vector<T>> : public Base<std::vector<T>>
{
public:
void print(void)
{
for (const auto& d : this->data)
{
std::cout << d << std::endl;
}
}
};
This solution works well. Are there some better or more conventional solutions?
Why does this member function specialization get error?
When you instantiate the template class A for example A<std::vector<int>>, the template parameter T is equal to std::vector<int>, not std::vector<T>, and this a specialization case of the function. Unfortunately this can not be done with member functions as mentioned in the comments.
Are there some better solutions?
Yes; In c++17 you could use if constexpr with a trait to check the std::vector, like this.
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
void print() const /* noexcept */
{
if constexpr (is_std_vector<T>::value) // when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else // for types other than `std::vector<>`
{
std::cout << mData << std::endl;
}
}
};
(See Live Online)
This way you keep only one template class and the print() will instantiate the appropriate part according to the template type T at compile time.
If you don not have access to C++17, other option is to SFINAE the members(Since c++11).
#include <type_traits> // std::false_type, std::true_type, std::enbale_if
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
template<typename Type = T> // when T == `std::vector<>`
auto print() const -> typename std::enable_if<is_std_vector<Type>::value>::type
{
for (const auto element : mData)
std::cout << element << "\n";
}
template<typename Type = T> // for types other than `std::vector<>`
auto print() const -> typename std::enable_if<!is_std_vector<Type>::value>::type
{
std::cout << mData << std::endl;
}
};
(See Live Online)
What if I have more other data types like self-define vector classes
or matrices? Do I have to define many is_xx_vector?
You can check the type is a specialization of the provided one like as follows. This way you can avoid providing many traits for each type. The is_specialization is basically inspired from this post
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// custom MyVector (An example)
template<typename T> struct MyVector {};
template<typename Test, template<typename...> class ClassType>
struct is_specialization final : std::false_type {};
template<template<typename...> class ClassType, typename... Args>
struct is_specialization<ClassType<Args...>, ClassType> final : std::true_type {};
And the print function could be in c++17:
void print() const /* noexcept */
{
if constexpr (is_specialization<T, std::vector>::value)// when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else if constexpr (is_specialization<T, ::MyVector>::value) // custom `MyVector`
{
std::cout << "MyVector\n";
}
else // for types other than `std::vector<>` and custom `MyVector`
{
std::cout << mData << std::endl;
}
}
(See Live Online)
You need to implement a template class that uses a vector as template parameter. This worked for me.
template<typename T>
class A
{
public:
T data;
void print(void) {
std::cout << "Data output" << std::endl;
}
// other functions ...
};
template <typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print() {
for (auto i : data) {
std::cout << "Vector output" << std::endl;
}
}
};
You could always use named tag dispatching to check if type provided by template user is vector.
A<std::vector<T>> notation won't work as you both try to take into account that T is type and vector of types which is contradicting with itself.
Below is code I used named tag dispatching as solution to your problem:
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};
template<typename T>
class A
{
public:
T data;
void print(std::true_type) {
for (auto& a : data) { std::cout << a << std::endl; }
}
void print(std::false_type) {
std::cout << data << std::endl;
}
void print() {
print(is_vector<T>{});
}
};
int main()
{
A<int> a;
a.data = 1;
a.print();
A<std::vector<int>> b;
b.data = { 1, 2 ,3 ,4 ,5 };
b.print();
return 0;
}
Succesfully compiled with https://www.onlinegdb.com/online_c++_compiler
Based on answer: Check at compile-time is a template type a vector
You can dispatch printing to another member function (static or not). For example:
template<typename T>
class A {
public:
T data;
void print() const {
print_impl(data);
}
private:
template<class S>
static void print_impl(const S& data) {
std::cout << data;
}
template<class S, class A>
static void print_impl(const std::vector<S, A>& data) {
for (const auto& d : data)
std::cout << d;
}
};

A better idiom for referring to base classes from derived classes?

Suppose I have a
template <typename T>
class A :
class_with_long_name<T, and_many_other, template_arguments, oh_my_thats_long>,
anotherclass_with_long_name<and_many_other, template_arguments_that, are_also_annoying, including_also, T> { ... }
Now, in class A's definition, and/or in its methods, I need to refer to the two superclasses (e.g. to access members in the superclass, or types defined in it etc.) However, I want to avoid repeating the superclass names. At the moment, what I'm doing is something like:
template<typename T>
class A :
class_with_long_name<T, and_many_other, template_arguments, oh_my_thats_long>,
anotherclass_with_long_name<and_many_other, template_arguments_that, are_also_annoying, including_also, T>
{
using parent1 = class_with_long_name<T, and_many_other, template_arguments, oh_my_thats_long>;
using parent2 = anotherclass_with_long_name<and_many_other, template_arguments_that, are_also_annoying, including_also, T>;
...
}
which works, obviously, and reduces the number of repetitions to 2; but I would rather avoid even this repetition, if possible. Is there a reasonable way to do this?
Notes:
"Reasonable" e.g. no macros except with very very good justification.
Before A, you may do
namespace detail
{
template <typename T>
using parentA1 = class_with_long_name<T,
and_many_other,
template_arguments,
oh_my_thats_long>;
template <typename T>
using parentA2 = anotherclass_with_long_name<and_many_other,
template_arguments_that,
are_also_annoying,
including_also,
T>;
}
And then
template<typename T>
class A : detail::parentA1<T>, detail::parentA2<T>
{
};
If you inherit with the same access specifier for all classes you could use something like this:
template <typename...S>
struct Bases : public S... {
template <size_t I>
using super = typename std::tuple_element<I, std::tuple<S...>>::type;
};
This will give you access to all base classes in the order you inherit from them via super<index>.
Short example:
#include <iostream>
#include <tuple>
template <typename...S>
struct Bases : public S... {
template <size_t I>
using super = typename std::tuple_element<I, std::tuple<S...>>::type;
};
class Foo
{
public:
virtual void f()
{
std::cout << "Foo";
}
};
class Fii
{
public:
virtual void f()
{
std::cout << "Fii";
}
};
class Faa : private Bases<Foo, Fii>
{
public:
virtual void f()
{
std::cout << "Faa";
super<0>::f(); //Calls Foo::f()
super<1>::f(); //Calls Fii::f()
std::cout << std::endl;
}
};
int main()
{
Faa faa;
faa.f(); //Print "FaaFooFii"
return 0;
}
I think, the best option is what you are already doing. However, if you feel like you absoluletely can not tolerate this, here some funny code (if I would ever see anything like this during code review in production, I'd fight tooth and nail to get rid of it).
template<class... INHERIT_FROM>
struct inherit_publicly : public INHERIT_FROM... {
struct parent_types {
template <int N, class ARG, class... ARGS> struct get {
using type = typename get<N-1, ARGS...>::type;
};
template <class ARG, class... ARGS> struct get<0, ARG, ARGS...> {
using type = ARG;
};
};
template <int N> using parent = typename parent_types::template get<N, INHERIT_FROM...>::type;
};
// **example usage**
struct X { static constexpr const char* const name = "X"; };
struct Y { static constexpr const char* const name = "Y"; };
struct Z { static constexpr const char* const name = "Z"; };
#include <iostream>
struct A : inherit_publicly<X, Y, Z> {
void print_parents() {
std::cout << "First parent type: " << parent<0>::name << "; second: " << parent<1>::name << "; third: " <<parent<2>::name<< "\n";
}
};
int main() {
A a;
a.print_parents();
}
Live demo: http://coliru.stacked-crooked.com/a/37cacf70bed41463
You can just use A::class_with_long_name or A::anotherclass_with_long_name.
template<typename T>
class A
: class_with_long_name<T, and_many_other, template_arguments, oh_my_thats_long>
, anotherclass_with_long_name<and_many_other, template_arguments_that, are_also_annoying, including_also, T>
{
// If you still want the typedefs.
using parent1 = typename A::class_with_long_name;
using parent2 = typename A::anotherclass_with_long_name;
// If you don't.
void foo() { A::class_with_long_name::bar(); }
};
Based on #Jarod's solution: How about an inside-the-detail subnamespace?
namespace detail { namespace A {
template <typename T>
using parent1 = class_with_long_name<T,
and_many_other,
template_arguments,
oh_my_thats_long>;
template <typename T>
using parent2 = anotherclass_with_long_name<and_many_other,
template_arguments_that,
are_also_annoying,
including_also,
T>;
} // namespace A
} // namespace detail
And then
template<typename T>
class A : detail::A::parent1<T>, detail::A::parent2<T>
{
};

'using' declaration as SFINAE

Could I use SFINAE (or another technique) for using declaration while private deriving from template class?
For better understanding see code below:
#include <iostream>
struct S1 {
void f() { std::cout << "S1::f\n"; }
};
struct S2 {
void f() { std::cout << "S2::f\n"; }
void g() { std::cout << "S2::g\n"; }
};
template <class T>
struct D : private T {
using T::f;
// using T::g; // need this only if T provides g() function
};
int main() {
D<S1>().f(); // ok. Prints 'S1::f'
D<S2>().f(); // ok. Prints 'S2::f'
D<S2>().g(); // fail. But wants to be ok and prints 'S2::g'
return 0;
}
How can I reach desired behaviour (if it possible)?
A variant of Bryan Chen's answer that looks uglier, but makes it easier to extend to multiple checks, and doesn't require duplicating the code that's shared between D<type-with-f> and D<type-without-f>, is to use an inheritance chain, where each step checks one additional member. The only duplication required is the inheritance of constructors, if appropriate.
struct A {
void f() { }
void g() { }
void i() { }
};
// The generic case. D<T, char[N]> simply provides what D<T, char[N+1]> provides.
template <typename T, typename U = char[1]>
struct D : D<T, char[sizeof(U) + 1]> {
using D<T, char[sizeof(U) + 1]>::D;
};
// The end of the chain. This is where T gets inherited. It declares all of its own
// specialisations as its friends, so that they can access other members of T.
template <typename T>
struct D<T, char[6]> : private T {
template <typename, typename>
friend struct D;
D(int) { }
void fun() { }
};
// Check for T::f.
template <typename T>
struct D<T, char[2 + !sizeof(&T::f)]> : D<T, char[3]> {
using D<T, char[3]>::D;
using T::f;
};
// Check for T::g.
template <typename T>
struct D<T, char[3 + !sizeof(&T::g)]> : D<T, char[4]> {
using D<T, char[4]>::D;
using T::g;
};
// Check for T::h.
template <typename T>
struct D<T, char[4 + !sizeof(&T::h)]> : D<T, char[5]> {
using D<T, char[5]>::D;
using T::h;
};
// Check for T::i.
template <typename T>
struct D<T, char[5 + !sizeof(&T::i)]> : D<T, char[6]> {
using D<T, char[6]>::D;
using T::i;
};
int main() {
D<A> d = 4; // ok: verify that constructors got inherited
// A &a = d; // error: verify that inheritance of A is private
d.f(); // ok: verify that f got inherited
d.g(); // ok: verify that g got inherited
// d.h(); // error: verify that h is not available
d.i(); // ok: verify that i got inherited
d.fun(); // ok: verify that the inheritance chain didn't get broken
}
Note: instead of checking &T::f, you may want to do something with std::declval<T>().f() instead. The former cannot handle overloaded functions.
C++ partial template specialization and use decltype(void(&T::g)) for SFINAE
#include <iostream>
#include <type_traits>
struct S1 {
void f() { std::cout << "S1::f\n"; }
};
struct S2 {
void f() { std::cout << "S2::f\n"; }
void g() { std::cout << "S2::g\n"; }
};
template <class T, class V = void>
struct D : private T {
using T::f;
};
template <class T>
struct D<T, decltype(void(&T::g))> : private T {
using T::f;
using T::g; // need this only if T provides g() function
};
int main() {
D<S1>().f(); // ok. Prints 'S1::f'
D<S2>().f(); // ok. Prints 'S2::f'
D<S2>().g(); // ok. Prints 'S2::g'
return 0;
}
Live Demo
Edit:
This is another approach that is more flexible, but I have no idea how does private virtual inheritance works with real use cases. Please let me know if it may cause any issue (e.g. UB).
#include <iostream>
#include <type_traits>
struct S1 {
void f() { std::cout << "S1::f\n"; }
};
struct S2 {
void f() { std::cout << "S2::f\n"; }
void g() { std::cout << "S2::g\n"; }
};
struct S3 {
void g() { std::cout << "S3::g\n"; }
};
template <class T, class = void>
struct D_f {};
template <class T>
struct D_f<T, decltype(void(&T::f))> : private virtual T {
using T::f;
};
template <class T, class = void>
struct D_g {};
template <class T>
struct D_g<T, decltype(void(&T::g))> : private virtual T {
using T::g;
};
template <class T>
struct D : D_f<T>, D_g<T> {
};
int main() {
D<S1>().f();
D<S2>().f();
D<S2>().g();
D<S3>().g();
return 0;
}
Live Demo