Template Metaprogramming Is Evil

So... I’m stumped. What am I doing wrong?

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
// Compile with C++11 or better

#include <iostream>
#include <set>
#include <type_traits>
#include <vector>

template <typename T>
constexpr typename std::enable_if <std::is_member_function_pointer <decltype(&T::count)> ::value, bool> ::type
has_count( const T& ) { return true; }

template <typename T>
constexpr bool
has_count( const T& ) { return false; }

int main()
{
  int               xs[] = { 1, 2, 3 };
  std::vector <int> ys     { 4, 5, 6 };
  std::set    <int> zs     { 7, 8, 9 };

  std::cout << std::boolalpha
    << "array  xs: " << has_count( xs ) << "\n"
    << "vector ys: " << has_count( ys ) << "\n"
    << "set    zs: " << has_count( zs ) << "\n";

  std::cout << "zs.count( 7 ) = " << zs.count( 7 ) << "\n";
}
$ a
array  xs: false
vector ys: false
set    zs: false
zs.count( 7 ) = 1

Why is SFINAE not selecting true for member function ‘zs::count’?

Tested with:

  cl /EHsc /Ox /W4 /std:c++17 a.cpp
  clang++ -O3 -Wall -pedantic-errors -std=c++17 a.cpp

No compile errors. It simply does not report “true” for zs.
What's the compiler?

Try a different compiler and see if it gives a different result?

Using 2 or more to confirm/deny is what I'd do.

For what that's worth. Not much since I'm practically a noob.
Would it be helpful, a hindrance or a wash that vanilla VS 2019 gives the same output?
Er, tested with MSVC 2019 and Clang 9.0. It isn’t the compiler. I’m making a mistake.
Oh, if making a mistake is what you want, then I'm your daisy!

I kinda grasp what you are wanting to do...see if a container has a count() member function, right?

How to do that is way above my current paygrade.
Yes, exactly.

Mine too.
Oh, my, you made me snort some rum and coke through my nose.

I'm still laughing, thanks. :)

When I first glanced at your code it was all Linear A & B to me.

I heard a few hundred brain cells cry out in terror and suddenly fell silent.....LO!

A faint inkling of what the first template is supposed to do descended from on high.

Learning is really not FUN-damental, just mental. *ouch*
Last edited on
What you have looks like:
1
2
U has_count( const T& ) { return true; }
V has_count( const T& ) { return false; }

Overload based on return type. Is that legal?

https://eli.thegreenplace.net/2014/sfinae-and-enable_if/
has example that essentially says:
1
2
3
4
5
6
template <
    typename T,
    typename std::enable_if something
  >
constexpr bool
has_count( const T& ) { return true; }

Introduce a dummy parameter to impose a preferred ordering:
1
2
3
4
5
6
7
8
9
10
11
template <typename T>
constexpr typename std::enable_if<
  std::is_member_function_pointer<decltype(&T::count)>::value,
  bool
>::type
has_count(T&&, int) { return true; }

template <typename T>
constexpr bool has_count(T&&, ...) { return false; }

// to use: has_count(thing, 0); 


You'll often want to use forwarding reference types and not T const& in your generic interfaces. This preserves value category and cv-qualification, which is important since the side-effects & value of an expression can completely change with those properties.
Last edited on
keskiverto wrote:
Overload based on return type. Is that legal?
good call: it isn't. and the program would not compile had it happened.

but, as of C++14, decltype(&T::count) fails for all three types provided. It fails for array and vector because they have no such member, and it fails for set because it has more than one such member. So there was never any overloading, only the second, false-returning, function template.

If you compile with gcc/clang -std=c++11, then you get the overloading and the expected
rror: call of overloaded 'has_count(std::set<int>&)' is ambiguous
Last edited on
it fails for set because it has more than one such member

Oh crud. Should have read the diagnostics and the docs more carefully. That was dumb.
In my defense, I was barely conscious...

This has set me on the right track: because count() is overloaded, all my efforts with SFINAE so far have failed, because they all worked by taking the address of the function...

Thanks!
BTW, if anyone is curious:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  template <typename T>
  struct has_count
  {
    private:
      template <typename U, typename = decltype( std::declval <U> ().count( std::declval <const typename U::value_type&> () ) )>
      static std::true_type test( int );
      
      template <typename U>
      static std::false_type test( ... );
      
    public:
      typedef decltype( test <T> ( 0 ) ) type;
      static constexpr bool value = type::value;
  };
BTW, if anyone is curious

Maybe later, I have to attend funerals for those dead brain cells first.
Hi,

You could try metashell for TMP stuff :+) http://metashell.org/

I know of it's existence only because JLBorges mentioned it sometime ago.
Thanks. I’ll check it out.
C++20 makes it less of an eyesore

1
2
3
4
5
6
template<typename T>
concept HasCount = requires(T a) { a.count( typename T::value_type{} ); };

constexpr bool has_count(const HasCount auto&) { return true; }

constexpr bool has_count( const auto& ) { return false; }
live demo: https://wandbox.org/permlink/nluEYD2c8HSzSBfn

EDIT: or perhaps
1
2
3
4
template<typename T>
concept HasCount = requires(T a, typename T::value_type v) {
        a.count(v);
};
- https://wandbox.org/permlink/UIZR7MSkaV6B2lfE - there are many options
Last edited on
Topic archived. No new replies allowed.