7

After this question by utilizing ADL one can create a trait to answer if the passed type comes from our namespace:

#include <utility>

namespace helper
{
  template <typename T, typename = void>
  struct is_member_of_sample : std::false_type
  {
  };

  template <typename T>
  struct is_member_of_sample<
      T,
      decltype(adl_is_member_of_sample(std::declval<T>()))> : std::true_type
  {
  };
}

namespace sample
{
  template <typename T>
  auto adl_is_member_of_sample(T && ) -> void;
}

// -- Test it

namespace sample
{
  struct X;
}

struct Y;

static_assert(helper::is_member_of_sample<sample::X>::value, "");
static_assert(not helper::is_member_of_sample<Y>::value, "");

int main(){}

From obvious reason this cannot be applied to the std namespace - there is simply no way to inject the adl_is_member_of_sample equivalent to the std namespace without exposing ourself to undefined behaviour.

Is there some workaround enabling to create the trait?

Community
  • 1
  • 1
W.F.
  • 13,888
  • 2
  • 34
  • 81
  • This sounds like an [xy problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Why do you need to do this? – Pete Becker Feb 04 '17 at 16:40
  • Note that a name can be in multiple namespaces for ADL purposes. `std::pair` is in three namespaces - std, foo, and bar. – Martin Bonner supports Monica Feb 04 '17 at 16:46
  • 1
    @PeteBecker The problem has origins [this question](http://stackoverflow.com/questions/41867111/make-stds-data-structure-use-my-existing-non-static-hash-function-hashcode/41977680#41977680), but I'm more curious about it because I think there is many more applications for it... – W.F. Feb 04 '17 at 16:51
  • @MartinBonner yep I haven't taken it into account... – W.F. Feb 04 '17 at 16:54
  • That's what I suspected: it's a workaround for a design mistake, i.e., an xy problem. Those hash functions should be free functions, not members. Then the "problem" disappears. – Pete Becker Feb 04 '17 at 16:57
  • @PeteBecker Well yes and no - I think the bigger problem is with `std::hash` which isn't sfinae ready... – W.F. Feb 04 '17 at 17:00
  • @Pete Becker Is it good if a hash function become "friend"-function inside a class? Will it be free-function? Will it still be a design mistake? – javaLover Feb 05 '17 at 05:01
  • 1
    @javaLover - "free function" is a different name for "non-member function". `std::hash` was designed as a set of free functions, and fighting that design just makes things harder. Whether it needs to be a friend depends on just what it does, but free function that's a friend is still a free function. – Pete Becker Feb 05 '17 at 11:29

1 Answers1

5

This seems to work:

#include <functional>
#include <type_traits>
#include <utility>
#include <string>

namespace other { struct S{}; }

namespace my {
    template< class Type >
    void ref( Type&& ) {}

    template< class Type >
    auto ref_to( Type&& o )
        -> Type&
    { return o; }

    template< class Type >
    constexpr auto is_std_type()
        -> bool
    {
        using std::is_same;
        using std::declval;
        return not is_same< void, decltype( ref( ref_to( declval<Type>() ) ) )>::value;
    }

    struct Blah {};

    constexpr bool int_is_std       = is_std_type<int>();
    constexpr bool blah_is_std      = is_std_type<Blah>();
    constexpr bool other_is_std     = is_std_type<other::S>();
    constexpr bool string_is_std    = is_std_type<std::string>();
};

#include <iostream>
using namespace std;
auto main()
    -> int
{
    cout << boolalpha;
    cout << "int is std = " << my::int_is_std << "\n";
    cout << "blah is std = " << my::blah_is_std << "\n";
    cout << "other is std = " << my::other_is_std << "\n";
    cout << "string is std = " << my::string_is_std << "\n";
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • WOW! I need to analyse it more but it looks brilliant! – W.F. Feb 04 '17 at 16:42
  • 2
    Well I think it may give you false positive for a type that *involves* a type from `std`, depending on how you define "is from", but I haven't tested. Have fun! :) – Cheers and hth. - Alf Feb 04 '17 at 16:51
  • Yes. If you add `template struct TT{};` to `other`, then it yields true for `other::TT`. I still agree with Pete Becker this sounds like an XY problem. – Martin Bonner supports Monica Feb 04 '17 at 16:55
  • The template parameter issue can be _mostly_ fixed by adding a specialisation for when `Type` is a templated class, [like so](http://rextester.com/VKNF84604). This still doesn't work properly when `Type` has any defaulted template parameters that default to a `std` type, though, because I wasn't sure how to remove those without potentially breaking stuff. Tested with Clang, GCC, and MSVC (had to replace `operator not` with `operator!` for the latter, though, because it was being childish). – Justin Time - Reinstate Monica Feb 04 '17 at 21:29
  • 3
    There's also classes derived from `std` classes to consider. Also, `ref_to( declval() )` is just `declval()`. – T.C. Feb 04 '17 at 23:50
  • @T.C.: Can't understand how I didn't think of `Type&`. Lols. :) Thank you. – Cheers and hth. - Alf Feb 05 '17 at 04:56