Checking if a function is const

Given

1
2
3
4
5
6
class A {
public:
	void foo() {}
	int foo (bool, char, double) {return 0;}
	bool foo (double, char) const {return true;}
};

HasFooOverloads<A, F<void, NotConst>, F<int, NotConst, bool, char, double>>::value is to evaluate to true because void A::foo() and int A::foo(bool, char, double) overloads exist. My HasFooOverloads traits class is working fine, except for checking the constness of a function. For example, HasFooOverloads<A, F<void, Const>>::value should evaluate to false because though void A::foo() exists, void A::foo() const does not. But it is evaluating to true instead.

This is what I have so far
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <type_traits>

class A {
public:
	void foo() {}
	int foo (bool, char, double) {return 0;}
	bool foo (double, char) const {return true;}
};

template <typename...> struct voider {using type = void;};

template <typename... Ts>
using void_t = typename voider<Ts...>::type;  // 'template <typename T> using void_t = void;' is ambiguous with some compilers.

enum Constness {Const, NotConst};

template <typename R, Constness, typename... Args> struct F;

template <typename T, typename, typename... Fs> struct HasFooOverloadsHelper;

template <typename T, typename R, typename... Args, typename... Rest>
struct HasFooOverloadsHelper<T, void_t<decltype(std::declval<T&>().foo(std::declval<Args>()...))>, F<R, Const, Args...>, Rest...> {
	static constexpr bool value = !std::is_same<R, decltype(std::declval</*const*/ T&>().foo(std::declval<Args>()...))>::value ?
		false : HasFooOverloadsHelper<T, void_t<T>, Rest...>::value;
};

template <typename T, typename R, typename... Args, typename... Rest>
struct HasFooOverloadsHelper<T, void_t<decltype(std::declval<T&>().foo(std::declval<Args>()...))>, F<R, NotConst, Args...>, Rest...> {
	static constexpr bool value = !std::is_same<R, decltype(std::declval<T&>().foo(std::declval<Args>()...))>::value ?
		false : HasFooOverloadsHelper<T, void_t<T>, Rest...>::value;
};

template <typename T>
struct HasFooOverloadsHelper<T, void_t<T>> : std::true_type {};  // Each template instantiation must have std::is_same<R, ...>::value == true for this to be reached.

template <typename T, typename... Fs>
using HasFooOverloads = HasFooOverloadsHelper<T, void_t<T>, Fs...>;

int main() {
	std::cout << std::boolalpha << HasFooOverloads<A, F<int, NotConst, bool, double>>::value << '\n';
		// false, because no int A::foo(bool, double) overload exists.
	std::cout << HasFooOverloads<A, F<int, NotConst, bool, char, double>>::value << '\n';
		// true, because a A::foo(bool, char, double) overload exists.
	std::cout << HasFooOverloads<A, F<int, NotConst, bool, char, double>, F<int, NotConst, bool, double>>::value << '\n';
		// false, again because no int A::foo(bool, double) overload exists.
	std::cout << HasFooOverloads<A, F<int, NotConst>>::value << '\n';
		// false, because no int A::foo() overload exists.
	std::cout << HasFooOverloads<A, F<void, Const>>::value << '\n';
		// true, but should be false, because though void A::foo() exists, void A::foo() const does not.
	std::cout << HasFooOverloads<A, F<void, NotConst>>::value << '\n';
		// true, because a void A::foo() overload exists. 
	std::cout << HasFooOverloads<A, F<void, NotConst>, F<int, NotConst, bool, char, double>>::value << '\n';
		// true, because void A::foo() and int A::foo(bool, char, double) overloads exist.
	std::cout << HasFooOverloads<A, F<void, NotConst>, F<int, NotConst, bool, char, double>, F<bool, Const, double, char>>::value << '\n';  // true
}


I think the problem is that the /*const*/ in the above code is needed, but it won't compile. Can anyone help make HasFooOverloads check for constness properly?
Last edited on
Using Boost Type Traits Introspection Library http://www.boost.org/doc/libs/1_59_0/libs/tti/doc/html/index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <boost/tti/has_member_function.hpp>

BOOST_TTI_HAS_MEMBER_FUNCTION(foo)

struct A
{
    void foo( int, long ) ;

    short foo( char, char, char ) const ;
    short foo( char, char, char ) ;

    int foo(double) const ;
};

int main()
{
    std::cout << std::boolalpha
              << has_member_function_foo< void(A::*)(int,long) const >::value << '\n' // false
              << has_member_function_foo< void(A::*)(int,long) >::value << "\n\n" // true

              << has_member_function_foo< short(A::*)(char,char,char) const >::value << '\n' // true
              << has_member_function_foo< short(A::*)(char,char,char) >::value << "\n\n" // true

              << has_member_function_foo< int(A::*)(double) const >::value << '\n' // true
              << has_member_function_foo< int(A::*)(double)  >::value << "\n\n" ; // false
}

http://coliru.stacked-crooked.com/a/dc4b5e08fc0a1cf4
Deleted
Last edited on
Thanks for pointing me out to Boost, JLBorges. I will use it if I have to, but this is mainly an intellectual exercise to improve my programming with templates. I've improved my previous solution, and now I believe it is working correctly. Many cases passed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <type_traits>

struct A {
	void foo (int) const {}
	void foo (int, bool, char) const {}
	void foo() const {}
	int foo (char, double) {return 0;}
	bool foo (int) {return false;}
};

enum Constness {Const, NotConst};

template <typename R, Constness, typename... Args> struct F;

template <typename T, typename Void, typename... Fs>
struct Check : std::false_type {};

template <typename T, typename R, typename... Args, typename... Rest>
struct Check<T, typename std::enable_if<
        std::is_same<R, decltype(std::declval<const T&>().foo(std::declval<Args>()...))>::value  // Check if the return type is R, else inherit from std::false_type.
    >::type, F<R, Const, Args...>, Rest...> : Check<T, void, Rest...> {};  // Check the next F.  Will check the std::enable_if conditions in case the specializations are met.

template <typename T, typename R, typename... Args, typename... Rest>
struct Check<T, typename std::enable_if<
        std::is_same<R, decltype(std::declval<T&>().foo(std::declval<Args>()...))>::value  // Check if the return type is R, else inherit from std::false_type.
    >::type, F<R, NotConst, Args...>, Rest...> : std::conditional<
			Check<T, void, F<R, Const, Args...>>::value,  // Check if foo is const.  If so, inherit from std::false_type.
        	std::false_type,
        	Check<T, void, Rest...>  // Check the next F.  Will check the std::enable_if conditions in case the specializations are met.
    >::type {};

template <typename T>
struct Check<T, void> : std::true_type {};  // Reached only when all the previous checks passed.

template <typename T, typename... Fs>
using CheckFooOverloads = Check<T, void, Fs...>;  // Will check the std::enable_if conditions in case the specializations are met.

int main() {
	std::cout << std::boolalpha << CheckFooOverloads<A, F<void, Const, int>, F<void, Const, int, bool, char>>::value << '\n';  // true, because void A::foo(int) const and void A::foo(int, bool, char) const overloads exist.
	std::cout << CheckFooOverloads<A, F<void, Const, int>, F<void, Const, int, bool, char>, F<void, Const>>::value << '\n';  // true, because void A::foo(int) const, void A::foo(int, bool, char) const, and void A::foo() const overloads exist
	std::cout << CheckFooOverloads<A, F<void, Const, int, bool>>::value << '\n';  // false, because no void A::foo(int, bool) const overload exists.
	std::cout << CheckFooOverloads<A, F<void, Const, int, bool>, F<void, Const, int, bool, char, long, float>>::value << '\n';  // false
	std::cout << CheckFooOverloads<A, F<void, Const, int>, F<void, Const, int, bool, char>, F<void, Const, int, bool, char, long, float>>::value << '\n';  // false (though the first two exist, the last does not).
	std::cout << CheckFooOverloads<A, F<void, Const, int>, F<void, Const, int, bool, char>, F<void, Const, int, bool>>::value << '\n';  // false, because though void A::foo(int) const and void A::foo(int, bool, char) const overloads exist, no void A::foo(int, bool) const overload exists.
	std::cout << CheckFooOverloads<A, F<int, NotConst, char, double>>::value << '\n';  // true, because int A::foo(char, double) exists (and is not const).
	std::cout << CheckFooOverloads<A, F<int, NotConst, char, double>, F<bool, NotConst, int>>::value << '\n';  // true, because int A::foo(char, double) and bool A::foo(int) overloads exist.
	std::cout << CheckFooOverloads<A, F<int, NotConst, char, double>, F<bool, NotConst, int>, F<void, Const, int, bool, char>>::value << '\n';  // true
	std::cout << CheckFooOverloads<A, F<int, NotConst, char, double>, F<void, Const, int>, F<bool, NotConst, int>, F<void, Const, int, bool, char>>::value << '\n';  // true
	std::cout << CheckFooOverloads<A, F<void, Const>, F<int, NotConst, char, double>, F<void, Const, int>, F<bool, NotConst, int>, F<void, Const, int, bool, char>>::value << '\n';  // true
	std::cout << CheckFooOverloads<A, F<void, NotConst>>::value << '\n';  // false, because though void A::foo() const exists, void A::foo() does not.
	std::cout << CheckFooOverloads<A, F<void, NotConst>, F<bool, Const, int>>::value << '\n';  // false
	std::cout << CheckFooOverloads<A, F<void, NotConst>, F<int, NotConst, char, double>, F<void, Const, int>, F<bool, NotConst, int>, F<void, Const, int, bool, char>>::value << '\n';  // false
}
Last edited on
Topic archived. No new replies allowed.