CppCon 2014: Walter E. Brown "Modern Template Metaprogramming: A Compendium, Part II"

By: CppCon

194   4   13864

Uploaded on 10/18/2014

http://www.cppcon.org

Presentation Slides, PDFs, Source Code and other presenter materials are available at: https://github.com/CppCon/CppCon2014
--
Template metaprogramming has become an important part of a C++ programmer's toolkit. This talk will demonstrate state-of-the-art metaprogramming techniques, applying each to obtain representative implementations of selected standard library facilities.

Along the way, we will look at void_t, a recently-proposed, extremely simple new type_traits candidate whose use has been described by one expert as "highly advanced (and elegant), and surprising even to experienced template metaprogrammers."
--
With broad experience in industry, academia, consulting, and research, Dr. Walter E. Brown has been a C++ programmer for over thirty years, joining the C++ standards effort in 2000. Among numerous other contributions, he is responsible for introducing such now-standard C++ library features as cbegin/cend and common_type as well as headers random and ratio , and has significantly impacted such core language features as alias templates, contextual conversions, and variable templates. He conceived and served as project editor for the International Standard on Special Mathematical Functions in C++.

When not playing with his grandchildren, Dr. Brown is an Emeritus participant in the C++ standards process, with several more core and library proposals under consideration. He was recently appointed an associate project editor for the C++ standard itself.
--
Videos Filmed & Edited by Bash Films: http://www.BashFilms.com

Comments (6):

By anonymous    2017-09-20

Another approach (from Walter Brown: Part I, Part II) is to use void_t.

Here's an example of a trait that checks if you can call emplace_back member function:

template <typename... T> struct make_void { typedef void type;};
template <typename... T> using void_t = typename make_void<T>::type;

template <typename, typename = void>
struct has_size : std::false_type { };

template <typename T>
struct has_size<T,
  void_t<decltype( std::declval<T&>().size() )>
> : std::true_type { };

int main() {
  std::cout << has_size<std::vector<int>>::value << std::endl;
}

This approach is a lot more concise than the one in my previous answer.

Original Thread

By anonymous    2017-09-20

SFINAE doesn't work here, as the class is already instantiated with T = int in do_have_size<int>::type. SFINAE works only for a list of template function candidates, in your case you'll get a hard error since in the instantiation

do_have_size<int>::type

the member function

template <typename = decltype(std::declval<int>().size())>
static std::true_type check(T);

is surely ill-formed for int. The

static std::false_type check(...);

won't ever be considered. So gcc is right here in rejecting your code and MSVC2017 should not accept the code.

Related: std::enable_if : parameter vs template parameter and SFINAE working in return type but not as template parameter

One solution is to use the magic of void_t (since C++17, but you can define your own in C++11/14), which maps any type list to void and enables crazy simple-looking SFINAE techniques, like so

#include <utility>
#include <vector>

template<typename...>
using void_t = void; // that's how void_t is defined in C++17

template <typename T, typename = void>
struct has_size : std::false_type {};

template <typename T>
struct has_size<T, void_t<decltype(std::declval<T>().size())>>
    : std::true_type {};

int main() {
    using TR = typename has_size<std::vector<int>>::type;
    using FL = typename has_size<int>::type;

    static_assert(std::is_same<TR, std::true_type>::value, "TRUE");
    static_assert(std::is_same<FL, std::false_type>::value, "FALSE");
}

Live on Wandbox

Here is a Cppcon video by Walter Brown which explains the void_t techniques in great detail, I highly recommend it!

Original Thread

By anonymous    2017-09-20

If you want to detect whether a type has a certain function or overloaded operator you have to call that function or operator. This is important because you might have several overloads of a function or operator and overload resolution always depends on the caller.

Here is a small example, based on CppCon 2014: Walter E. Brown "Modern Template Metaprogramming: A Compendium, Part II" on how to detect operator[] in a type.

I have no idea why VC is giving you such a weird error which looks more like a parsing error. I would have expected something like »reference to overloaded function could not be resolved; did you mean to call it?«.

#include <string>
#include <type_traits>
#include <vector>

// in C++17 std::void_t
template < typename... >
using void_t = void;


template < typename T, typename Index >
using subscript_t = decltype(std::declval<T>()[std::declval<Index>()]);

template < typename, typename Index = size_t, typename = void_t<> >
struct has_subscript : std::false_type {};

template < typename T, typename Index >
struct has_subscript< T, Index, void_t< subscript_t<T,Index> > > : std::true_type {};


struct A
{
  void operator[](size_t) {}
};

struct B {};

int main ()
{
  static_assert(has_subscript< std::vector<int> >::value    == true , "!");
  static_assert(has_subscript< std::vector<double> >::value == true , "!");
  static_assert(has_subscript< A >::value                   == true , "!");
  static_assert(has_subscript< A, std::string >::value      == false, "!");
  static_assert(has_subscript< B >::value                   == false, "!");
  static_assert(has_subscript< double[5] >::value           == true , "!");
  static_assert(has_subscript< double* >::value             == true , "!");
  static_assert(has_subscript< double >::value              == false, "!");
}

Original Thread

Popular Videos 55896

Submit Your Video

If you have some great dev videos to share, please fill out this form.