阅读相关问题"How to call member function only if object happens to have it?"和 "Is it possible to write a C++ template to check for a function's existence?" ,我正在实现自己的特质类(class)。目标非常简单,尽管我无法实现我想要的:提供一个将调用静态重定向到匹配类的特征类。
所以,如果我提供给我的特征的类有,例如 void open_file()
方法,它调用它,否则使用 traits 函数(一个 NOP
一个,但现在是一个输出)。显然,这是一个 SFINAE 任务,但由于对过程不太熟悉,我遵循了这些想法,如您所见。
void open_file()
一切正常, 但在尝试 void open_file(int)
时,它不匹配并调用 NOP
功能。这是我的尝试(这两个问题几乎一字不差!):
template <class Type>
class my_traits
{
//! Implements a type for "true"
typedef struct { char value; } true_class;
//! Implements a type for "false"
typedef struct { char value[2]; } false_class;
//! This handy macro generates actual SFINAE class members for checking event callbacks
#define MAKE_MEMBER(X) \
public: \
template <class T> \
static true_class has_##X(decltype(&T::X)); \
\
template <class T> \
static false_class has_##X(...); \
public: \
static constexpr bool call_##X = sizeof(has_##X<Type>(0)) == sizeof(true_class);
MAKE_MEMBER(open_file)
public:
/* SFINAE foo-has-correct-sig :) */
template<class A, class Buffer>
static std::true_type test(void (A::*)(int) const)
{
return std::true_type();
}
/* SFINAE foo-exists :) */
template <class A>
static decltype(test(&A::open_file)) test(decltype(&A::open_file), void *)
{
/* foo exists. What about sig? */
typedef decltype(test(&A::open_file)) return_type;
return return_type();
}
/* SFINAE game over :( */
template<class A>
static std::false_type test(...)
{
return std::false_type();
}
/* This will be either `std::true_type` or `std::false_type` */
typedef decltype(test<Type>(0, 0)) type;
static const bool value = type::value; /* Which is it? */
/* `eval(T const &,std::true_type)`
delegates to `T::foo()` when `type` == `std::true_type`
*/
static void eval(Type const & t, std::true_type)
{
t.open_file();
}
/* `eval(...)` is a no-op for otherwise unmatched arguments */
static void eval(...)
{
// This output for demo purposes. Delete
std::cout << "open_file() not called" << std::endl;
}
/* `eval(T const & t)` delegates to :-
- `eval(t,type()` when `type` == `std::true_type`
- `eval(...)` otherwise
*/
static void eval(Type const &t)
{
eval(t, type());
}
};
class does_match
{
public:
void open_file(int i) const { std::cout << "MATCHES!" << std::endl; };
};
class doesnt_match
{
public:
void open_file() const { std::cout << "DOESN'T!" << std::endl; };
};
如你所见,我已经实现了这两个,第一个使用宏 MAKE_MEMBER
只需检查成员是否存在,它就可以工作。接下来,我尝试将它用于静态 SFINAE if/else
,即,如果一个成员函数存在则调用它,否则使用预定义的函数,但没有成功(正如我所说,我对 SFINAE 不太了解)。
第二次尝试几乎是从check signature and existence 问题开始的,但我修改了它以处理参数。但是,它不起作用:
does_match it_does;
doesnt_match it_doesnt;
my_traits<decltype(it_does)>::eval(it_does);
my_traits<decltype(it_doesnt)>::eval(it_doesnt);
// OUTPUT:
// open_file() not called
// open_file() not called
很明显,这里有问题:我没有提供参数,但我不知道怎么办。
我正在尝试理解和学习,另外,我可以使用 open_file()
吗?这取决于模板参数,例如具有匹配 template <class T> open_file(T t)
的 SFINAE ?
谢谢和干杯!
最佳答案
问题是您对 test(&A::open_file)
的调用:
typedef decltype(test(&A::open_file)) return_type;
总是匹配到:
static std::false_type test(...)
因为你的 true-test 有一个未推导的类型模板参数 Buffer
:
template<class A, class Buffer>
// ~~~~~^
static std::true_type test(void (A::*)(int) const)
因此,它永远不会被视为一个可行的函数,除非您明确提供该类型参数,或将其删除(此处应执行的操作)。
解决这个问题仍然不能解决你代码的所有问题,因为如果 open_file
成员函数根本不存在,你就没有可以选择的回退函数,所以你需要添加如下内容(根据您的实现进行调整):
/* SFINAE foo-not-exists */
template <class A>
static std::false_type test(void*, ...);
作为后备:
static decltype(test(&A::open_file)) test(decltype(&A::open_file), void *)
提示:您不必提供仅出现在未计算上下文中的函数体,例如在 decltype()
运算符中。
最后,当您最终将调用与 void (A::*)(int) const
签名匹配时,您似乎忘记了参数:
t.open_file(1);
// ^
测试:
my_traits<decltype(it_does)>::eval(it_does);
my_traits<decltype(it_doesnt)>::eval(it_doesnt);
输出:
MATCHES!
open_file() not called
使用 表达式 SFINAE 可以大大简化整个特征:
template <class Type>
struct my_traits
{
template <typename T>
static auto eval(const T& t, int) -> decltype(void(t.open_file(1)))
{
t.open_file(1);
}
template <typename T>
static void eval(const T& t, ...)
{
std::cout << "open_file() not called" << std::endl;
}
static void eval(const Type& t)
{
eval<Type>(t, 0);
}
};
my_traits<decltype(it_does)>::eval(it_does); // MATCHES!
my_traits<decltype(it_doesnt)>::eval(it_doesnt); // open_file() not called
奖励问题
Is it possible to generalize the approach? For instance given any function f use SFINAE to match it, using the code you posted in DEMO 2, and passing parameters from the user code (e.g., my_traits::eval(it_does, parameter, parameter))?
template <typename T, typename... Args>
static auto call(T&& t, int, Args&&... args)
-> decltype(void(std::forward<T>(t).open_file(std::forward<Args>(args)...)))
{
std::forward<T>(t).open_file(std::forward<Args>(args)...);
}
template <typename T, typename... Args>
static void call(T&& t, void*, Args&&... args)
{
std::cout << "open_file() not called" << std::endl;
}
template <typename T, typename... Args>
static void eval(T&& t, Args&&... args)
{
call(std::forward<T>(t), 0, std::forward<Args>(args)...);
}
eval(it_does, 1); // MATCHES!
eval(it_doesnt, 2); // open_file() not called
eval(it_does); // open_file() not called
eval(it_doesnt); // DOESN'T!
关于c++ - 匹配成员函数存在和签名 : parameters,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27632584/