如何仅在类具有特定方法时才调用特定方法?
How to call a specific method only if class has one?
我有一个不同类的对象元组。我想遍历元组,并且只有在这些类有一个时调用某个方法。
例如(伪代码):
struct A { int get( ) { return 5; }; };
struct B { };
struct C { int get( ) { return 10; }; };
int i = 0;
tuple<A, B, C> t;
for ( auto t_element : t )
{
if constexpr ( has_get_method( decltype(t_element) ) )
{
i += t_element.get( );
}
}
我已经知道如何遍历元组并检查一个类是否有一些使用 sfinae 的方法,但是如何跳过没有所需方法的对象?
编辑:如果您将来发现这个老问题,请知道现在使用C++20标准的概念可以更轻松地完成此操作。
只需编写一个 sfinae'd 函数和一个包罗万象,以防前一个函数失败。您不必为此使用if constexpr
,也不能在 C++14 中实际使用它(这就是您标记问题的方式)。
下面是一个最小的工作示例:
#include <tuple>
#include <iostream>
auto value(...) { return 0; }
template <typename T>
auto value(T &t) -> decltype(t.get()) {
return t.get();
}
struct A { int get() { return 5; }; };
struct B {};
struct C { int get() { return 10; }; };
int main() {
int i = 0;
std::tuple<A, B, C> t;
i += value(std::get<0>(t));
i += value(std::get<1>(t));
i += value(std::get<2>(t));
std::cout << i << std::endl;
}
看到它在魔杖盒上启动并运行。
如果您有任何参数要用于测试它,可以使用std::forward
:
template <typename T, typename... Args>
auto value(T &t, Args&&... args)
-> decltype(t.get(std::forward<Args>(args)...))
{ return t.get(std::forward<Args>(args)...); }
然后调用它为:
i += value(std::get<0>(t), params);
你可以创建一个类型特征,通过声明几个函数来检查一个类是否有get()
方法(不需要定义它们)
template <typename>
constexpr std::false_type withGetH (long);
template <typename T>
constexpr auto withGetH (int)
-> decltype( std::declval<T>().get(), std::true_type{} );
template <typename T>
using withGet = decltype( withGetH<T>(0) );
下面是一个完全工作的 c++17 示例
#include <tuple>
#include <iostream>
#include <type_traits>
template <typename>
constexpr std::false_type withGetH (long);
template <typename T>
constexpr auto withGetH (int)
-> decltype( std::declval<T>().get(), std::true_type{} );
template <typename T>
using withGet = decltype( withGetH<T>(0) );
struct A { int get( ) { return 5; }; };
struct B { };
struct C { int get( ) { return 10; }; };
template <typename T>
int addGet (T & t)
{
int ret { 0 };
if constexpr ( withGet<T>{} )
ret += t.get();
return ret;
}
int main ()
{
int i = 0;
std::tuple<A, B, C> t;
i += addGet(std::get<0>(t));
i += addGet(std::get<1>(t));
i += addGet(std::get<2>(t));
std::cout << i << std::endl;
}
如果不能使用if constexpr
,则可以使用标记调度编写(在 c++11/14 中)getAdd()
template <typename T>
int addGet (T & t, std::true_type const &)
{ return t.get(); }
template <typename T>
int addGet (T & t, std::false_type const &)
{ return 0; }
template <typename T>
int addGet (T & t)
{ return addGet(t, withGet<T>{}); }
--编辑--
OP问
我的代码需要检查带有参数的模板化方法。是否可以修改您的解决方案,以便它可以检查类似
template<class T, class U> void process(T& t, U& u)
的内容,而不是 intget()
?
我想这是可能的。
您可以创建一个类型特征withProcess2
(其中 2 是参数数),该类型特征接收三个模板类型参数:类和两个模板类型
template <typename, typename, typename>
constexpr std::false_type withProcess2H (long);
template <typename T, typename U, typename V>
constexpr auto withProcess2H (int)
-> decltype( std::declval<T>().process(std::declval<U>(),
std::declval<V>()),
std::true_type{} );
template <typename T, typename U, typename V>
using withProcess2 = decltype( withProcess2H<T, U, V>(0) );
下面是一个完全工作的示例(c++17,但现在您知道如何使其成为 c++14),其中包含具有A
C
和具有 2 个模板参数的process()
和具有仅具有 1 个模板参数的process()
B
。
#include <iostream>
#include <type_traits>
template <typename, typename, typename>
constexpr std::false_type withProcess2H (long);
template <typename T, typename U, typename V>
constexpr auto withProcess2H (int)
-> decltype( std::declval<T>().process(std::declval<U>(),
std::declval<V>()),
std::true_type{} );
template <typename T, typename U, typename V>
using withProcess2 = decltype( withProcess2H<T, U, V>(0) );
struct A
{
template <typename T, typename U>
void process(T const &, U const &)
{ std::cout << "A::process(T, U)" << std::endl; }
};
struct B
{
template <typename T>
void process(T const &)
{ std::cout << "B::process(T)" << std::endl; }
};
struct C
{
template <typename T, typename U>
void process(T &, U &)
{ std::cout << "C::process(T, U)" << std::endl; }
};
template <typename T>
void callProcess (T & t)
{
static int i0 { 0 };
static long l0 { 0L };
if constexpr ( withProcess2<T, int &, long &>{} )
t.process(i0, l0);
}
int main ()
{
std::tuple<A, B, C> t;
callProcess(std::get<0>(t)); // print A::process(T, U)
callProcess(std::get<1>(t)); // no print at all
callProcess(std::get<2>(t)); // print C::process(T, U)
}
- 如何强制从重写方法调用重写的方法基方法?
- C++:使用方法调用析构函数的顺序是什么?
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 使用 object 中的方法调用带有 std::bind 和 std::function.target 的 C 样式函数
- 指向类方法调用的指针
- 如何使用 SFINAE 在方法调用中有条件地定义变量?
- 是否有可以处理方法调用依赖关系的设计模式?
- 如何缩短C++中的方法调用?
- 从部分专用模板方法调用模板非静态方法
- 有没有办法禁止派生类中的基类方法调用?
- 为什么这C++只在编译器上编码一个不明确的方法调用Microsoft?
- 从父方法调用子方法
- 如何将子方法调用到父方法
- 虚拟函数在哪里使用 vpointer to vtable 来解析方法调用,非虚拟方法存储在哪里以及如何解析它们?
- 从静态方法调用静态函数指针
- 从同一类中的另一个方法调用方法时出错
- 方法调用意外地像 l 值一样起作用
- 无法从派生的一个方法调用基类方法
- 从类方法调用命名空间中名为 Same 的函数时,重载解析失败
- C 多线程JAVA JNI方法调用