用于检测函数是否存在的C ++元函数的问题

时间:2017-08-23 11:07:00

标签: c++ c++14 metaprogramming template-meta-programming

我对C ++元函数有疑问,我不明白。我正在使用C ++ 14编译Apple的clang 8.1.0版本。说明问题的工作代码如下。

我从其他地方抄袭了一个元功能,我试图使用它。它旨在检测名为' bananify'具有传递给元函数的类型的参数。你称之为......

BananifyDetector<int>::value 
如果

可以看到表单的声明函数,则它应该返回true。

bananify(int)

问题是,只有在BananifyFinder的模板定义之前声明要搜索的函数时,它才有效,而不是 instantiation 。所以在我的示例代码中我会期望两者,

BananifyFinder<int>
BananifyFinder<std::string> 

已成功使用下面的代码,但由于bananify(std :: string)的定义,它失败了。

这很令人沮丧,好像我把函数检测器放在头文件中我必须在客户端代码中包含顺序感知,这是一种深刻的痛苦,在某些情况下可能无法正确。

我不确定这里发生了什么。它是一个C ++特性,一个铿锵的错误还是一些我做过的蠢事?

任何帮助表示感谢。

#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> ");
}

3 个答案:

答案 0 :(得分:1)

模板分两个阶段进行解析。

在第一个中,解析了独立于模板参数的表达式。在第二个中,解析了依赖于模板的参数依赖项,其中Argument dependent lookup被执行。

decltype(bananify(std::declval<A1>()))>是依赖于参数的构造(取决于A1)。

来自this page

  

ADL检查具有外部链接的函数声明   从模板定义上下文和模板中都可见   实例化上下文(换句话说,添加一个新函数   模板定义后的声明不会使其可见,除了   通过ADL)。

因此,您的代码会查看std::(使用ADL)并且找不到bananify函数。

在模板实例化之前移动它足以使其有资格进行查找。

答案 1 :(得分: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
}

答案 2 :(得分:0)

其他人留下了答案,但似乎已被删除。这是模板的名称相关查找问题。

在此处Name lookups in C++ templates

以及此处的详细信息http://en.cppreference.com/w/cpp/language/adl