3
\$\begingroup\$

From https://stackoverflow.com/a/257382/6949852 I learned how to check whether a member function exists, then I re-write it to standardize the codes:

Now the codes become:

#include <type_traits>
#include <iostream>

struct A  //has hello
{
  void hello() {}
};

struct B {}; //no hello

template<typename T>
struct has_hello
{
private:
  using yes = char;
  using no = long;
  template<typename C>
  static yes has(decltype(&C::hello));
  template<typename C>
  static no has(...);
public:
  enum name : bool {value = std::is_same<decltype(has<T>(nullptr)), yes>::value};
};

int main()
{
  std::cout << static_cast<std::underlying_type<has_hello<A>::name>::type>(has_hello<A>::value) << std::endl;
  std::cout << static_cast<std::underlying_type<has_hello<B>::name>::type>(has_hello<B>::value) << std::endl;
}

Compared to original codes:

  1. 0 -> nullptr
  2. enum -> enum name : type
  3. sizeof -> is_same
  4. typeof -> decltype
\$\endgroup\$
5
  • \$\begingroup\$ It would be great to keep updates invisible, e.g. just don't mention anything happened. CR focuses on reviewing particular version of the code, and it becomes harder if the target is moving. You can add more tests/docs/explanation if you'd like though. \$\endgroup\$ May 20, 2018 at 8:49
  • \$\begingroup\$ @Incomputable Ok, thx \$\endgroup\$
    – Chen Li
    May 20, 2018 at 8:53
  • 1
    \$\begingroup\$ I recommend you to hold on with accepting an answer, as it discourages other people from posting their answers. Usually 1-2 days if wait time is enough. \$\endgroup\$ May 20, 2018 at 16:44
  • \$\begingroup\$ @Incomputable Yes, what you said is reasonable, thx again \$\endgroup\$
    – Chen Li
    May 20, 2018 at 23:42
  • \$\begingroup\$ see en.cppreference.com/w/cpp/experimental/is_detected \$\endgroup\$
    – JDługosz
    May 21, 2018 at 5:56

1 Answer 1

5
\$\begingroup\$

Old technique

The version you're using relies on overload resolution. The current form of detection idiom relies on SFINAE. IIRC SFINAE didn't exist pre-C++11.(Thanks to @JDlugosz)

Code Review

  • Inherit from std::true_type and std::false_type

    That will spare a lot of boilerplate, and incorporates with tag dispatch quite well. It might not be directly usable in your case without some indirection and hiding into namespace detail.

  • Don't use enum as a member where static constexpr bool will do

    Even though with freestanding values it is a different story due to linkage. bool will just bring less surprises.

  • Provide xxx_v value aliases

Somewhat modern approach

Lets take somewhat more interesting example.

Identify if value of type Element can be push_backed into container of type Container.

Today, it is suggested to first "carve out" the operation itself into an alias:

template <typename Container, typename Element>
using pushback_t = decltype(std::declval<Container>().push_back(std::declval<Element>()));

Then, std::void_t is used:

template <typename Container, typename Element, typename = std::void_t<>>
struct has_pushback : std::false_type{};

template <typename Container, typename Element>
struct has_pushback<Container, Element, std::void_t<pushback_t<Container, Element>>>:
                        std::true_type{};

And then _v value alias is provided:

template <typename Container, typename Element>
inline constexpr bool has_pushback_v = has_pushback<Container, Element>::value;

Demo.

Shameless self PR: if you're a bit confused about how SFINAE works in the new version, I recommend reading this question which I've asked in the past.

Better upgrade path

When std::is_detected will arrive, one can easily put something like this (provided a copy the operation alias from above):

template <typename Container, typename Element>
struct has_pushback : std::is_detected<pushback_t, Container, Element>
{};

That will spare even more boilerplate. See comments for already existing implementations.

\$\endgroup\$
4
  • \$\begingroup\$ Thx for your answer I'll try these approach after my ddl today \$\endgroup\$
    – Chen Li
    May 20, 2018 at 23:45
  • 1
    \$\begingroup\$ ⟪IIRC SFINAE didn't exist pre-C++11.⟫ It most certainly did! I think you are thinking of expression SFINAE, in the body of a decltype unevaluated expression. \$\endgroup\$
    – JDługosz
    May 21, 2018 at 5:55
  • 1
    \$\begingroup\$ "when it will arrive…" see my file to use in the mean time. \$\endgroup\$
    – JDługosz
    May 21, 2018 at 5:59
  • \$\begingroup\$ Consider adding that this tests for the existence of a single lone member with the name, instead of at least one. \$\endgroup\$ May 14, 2019 at 4:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.