C ++ metaphone issues to detect if function exists

I have a problem with a C ++ meta function that I don't understand. I am building an assembly of Apple clang 8.1.0 using C ++ 14. The following is the working code illustrating the problem.

I rolled up the metafound from another place and I'm trying to use it. It is designed to detect functions called "bananify" that have a type parameter passed to the metafile. You name it ...

BananifyDetector<int>::value 

should return true if it can see the declared form function ...

bananify(int)

The problem is that it only works if the desired function is declared before defining the BananifyFinder template, as opposed to creating it. So in my code example, I would expect both

BananifyFinder<int>
BananifyFinder<std::string> 

to succeed with the code I have below, but due to where bananify (std :: string) was defined, it fails.

This is disappointing, as if I were placing the detector functions in the header files, I should include the order known in the client code, which is a serious pain and it may not be possible to get the right in some circumstances.

I'm not sure what is going on here. Is it a C ++ function, a bug in clang, or something dumb that I did?

Any help was appreciated.

#include <iostream>
#include <type_traits>   
////////////////////////////////////////////////////////////////////////////////
// A bananify function to be detected
// This is successfully found.
double bananify(int)
{
  return 0.0;
}

/// A meta function that detects if a single argument function named 'bananify'
/// exists with takes an argument of the type passed to the metafunction.
///
/// Note, automatic casts will get in the way occasionally so if function
/// bananify(float) exists, a BananifyFinder<int>::value will return true.
template<class ARG1>
class BananifyFinder {
private :
  template<typename ...> using VoidT_ = void;

  template<typename A1, typename = void>
  struct Test_ : std::false_type
  {
    typedef void returnType;
  };

  template<typename A1>
  struct Test_<A1, VoidT_<decltype(bananify(std::declval<A1>()))>> : std::true_type
  {
    typedef decltype(bananify(std::declval<A1>())) returnType;
  };

public :
  typedef typename Test_<ARG1>::returnType returnType;
  constexpr static bool value = Test_<ARG1>::value;
};

////////////////////////////////////////////////////////////////////////////////
// A bananify function to be detected that takes std::strings
// This fails to be found, but if we move it before the declaration of BananifyFinder it
// will be found;
std::string bananify(std::string)
{
  return "s";
}

// dummy class with no bananify function to be found
class Nothing{};

// check the results of the metafunction 'T'
template<class T>
void CheckBanana(const std::string &str)
{
  using DetectedType = BananifyFinder<T>;
  std::cout << str << " detected is " << DetectedType::value << std::endl;
  std::cout << str << " returns is " << typeid(typename DetectedType::returnType).name() << std::endl << std::endl;
}

////////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
  // this should print "BananifyFinder<int> 1 d"
  CheckBanana<int>("BananifyFinder<int> ");

  // this should print "BananifyFinder<std::string>  1 NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE"
  // but it prints "BananifyFinder<std::string>  0 v"
  // FAILS
  CheckBanana<std::string>("BananifyFinder<std::string> ");

  // this should print "BananifyFinder<Nothing>  0 v"
  CheckBanana<Nothing>("BananifyFinder<Nothing> ");
}
+4
source share
3 answers

The template is analyzed in two phases.

, . , .

decltype(bananify(std::declval<A1>()))> ( A1).

ADL , , ( , , ADL).

, std:: ( ADL) bananify.

, .

+1

, bananify , . , .

, , , :

#include <iostream>
#include <type_traits>
#include <typeinfo>

class A {
  public:
  double bananify(int)
  {
    return 0.0;
  }
};

// Find bananify(ARG1) as a member of C:
template<class C, class ARG1>
class BananifyFinder {
private :
  template<typename ...> using VoidT_ = void;

  template<typename A1, typename = void>
  struct Test_ : std::false_type
  {
    typedef void returnType;
  };

  template<typename A1>
  struct Test_<A1, VoidT_<decltype(std::declval<C>().bananify(std::declval<A1>()))>> : std::true_type
  {
    typedef decltype(std::declval<C>().bananify(std::declval<A1>())) returnType;
  };

public :
  typedef typename Test_<ARG1>::returnType returnType;
  constexpr static bool value = Test_<ARG1>::value;
};

class B {
  public:
  std::string bananify(std::string)
  {
    return "s";
  }
};


// check the results of the metafunction 'T'
template<class C, class T>
void CheckBanana(const std::string &str)
{
  using DetectedType = BananifyFinder<C,T>;
  std::cout << str << " detected is " << DetectedType::value << std::endl;
  std::cout << str << " returns is " << typeid(typename DetectedType::returnType).name() << std::endl << std::endl;
}


int main(int argc, char *argv[])
{
  CheckBanana<A,int>("BananifyFinder<int> "); // ok
  CheckBanana<B,std::string>("BananifyFinder<std::string> "); // ok
}
+1

Source: https://habr.com/ru/post/1684293/


All Articles