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

Issues with a C++ metafunction to detect if a function exists

本文关键字:函数 C++ 问题 存在 检测 是否 用于      更新时间:2023-10-16

我有一个不理解的C++元函数的问题。我正在使用 C++14 编译 Apple 的 clang 8.1.0 版本。说明该问题的工作代码如下。

我已经从其他地方抄了一个元函数,我正在尝试使用它。它旨在检测名为"bananify"的函数,这些函数具有传递给元函数的类型参数。你称之为...

BananifyDetector<int>::value 

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

bananify(int)

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

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

模板分两个阶段解析。

在第一个中,解析独立于模板参数的表达式。在第二个中,在执行参数依赖查找的位置解析模板依赖参数依赖

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

从此页面

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

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

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

我相信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
}

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

此处解决 C++模板中的名称查找

以及更多细节 这里 http://en.cppreference.com/w/cpp/language/adl